共用方式為


步驟 3:實作 Frame-Grabbing 函式

[與此頁面相關聯的功能,DirectShow是舊版功能。 它已被 MediaPlayerIMFMediaEngine、以及媒體基礎 的音訊/視訊擷取所取代。 這些功能已針對 Windows 10 和 Windows 11 進行優化。 Microsoft強烈建議新程式代碼盡可能在媒體 基礎中使用 MediaPlayerIMFMediaEngine 音訊/視訊擷取,而不是 DirectShow。 Microsoft建議使用舊版 API 的現有程式代碼,盡可能改寫成使用新的 API。]

[此 API 不受支持,未來可能會改變或無法使用。]

本主題是 擷取海報框架的步驟 3

下一個步驟是實作 GetBitmap 函式,該函式會使用媒體偵測器來擷取海報框架。 在此函式內,執行下列步驟:

  1. 建立媒體偵測器。
  2. 指定媒體檔案。
  3. 檢查檔案中的每個數據流。 如果是視訊串流,請取得影片的原生維度。
  4. 取得海報圖文框,指定搜尋時間和目標維度。

呼叫 CoCreateInstance來建立媒體偵測器物件:

CComPtr<IMediaDet> pDet;
hr = pDet.CoCreateInstance(__uuidof(MediaDet));

使用 IMediaDet::put_Filename 方法來指定檔名。 此方法會採用 BSTR 參數。

hr = pDet->put_Filename(bstrFilename);

取得串流的數量,並遍歷每個串流以檢查其媒體類型。 IMediaDet::get_OutputStreams 方法會擷取串流數目,而 IMediaDet::put_CurrentStream 方法會指定要檢查的串流。 在第一個視訊串流上結束迴圈。

long lStreams;
bool bFound = false;
hr = pDet->get_OutputStreams(&lStreams);
for (long i = 0; i < lStreams; i++)
{
    GUID major_type;
    hr = pDet->put_CurrentStream(i);
    hr = pDet->get_StreamType(&major_type);
    if (major_type == MEDIATYPE_Video)  // Found a video stream.
    {
        bFound = true;
        break;
    }
}
if (!bFound) return VFW_E_INVALIDMEDIATYPE;

如果找不到視訊串流,函式就會結束。

在先前的程式代碼中,IMediaDet::get_StreamType 方法只會傳回主要類型 GUID。 如果您不需要檢查完整的媒體類型,這會很方便。 不過,若要取得視訊尺寸,必須檢查格式區塊,因此需要完整的媒體類型。 您可以透過呼叫 IMediaDet::get_StreamMediaType 方法來擷取資料,該方法會填入 AM_MEDIA_TYPE 結構。 媒體偵測器會將所有視訊串流轉換成未壓縮的格式,並具有 VIDEOINFOHEADER 格式區塊。

long width = 0, height = 0; 
AM_MEDIA_TYPE mt;
hr = pDet->get_StreamMediaType(&mt);
if (mt.formattype == FORMAT_VideoInfo) 
{
    VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)(mt.pbFormat);
    width = pVih->bmiHeader.biWidth;
    height = pVih->bmiHeader.biHeight;
    
    // We want the absolute height, and don't care about up-down orientation.
    if (height < 0) height *= -1;
}
else {
    return VFW_E_INVALIDMEDIATYPE; // This should not happen, in theory.
}
FreeMediaType(mt);

get_StreamMediaType 方法會配置格式區塊,呼叫端必須釋放它。 此範例會使用基類連結庫中的 FreeMediaType 函式。

現在您已準備好取得海報框。 首先,呼叫 IMediaDet::GetBitmapBits 方法,並以緩衝區的 NULL 指標作為參數:

long lSize;
hr = pDet->GetBitmapBits(0, &lSize, NULL, width, height);

此呼叫會傳回 lSize 參數中所需的緩衝區大小。 第一個參數會指定要搜尋的數據流時間;此範例使用時間零。 針對寬度和高度,此範例會使用稍早取得的原生視訊尺寸。 如果您指定其他值,媒體偵測器會延展位圖以符合。

如果方法成功,請配置緩衝區並再次呼叫 GetBitmapBits

if (SUCCEEDED(hr)) 
{
    char *pBuffer = new char[lSize];
    if (!pBuffer) return E_OUTOFMEMORY;
    hr = pDet->GetBitmapBits(0, NULL, pBuffer, width, height);
    if (FAILED(hr))
        delete [] pBuffer;
}

以下是完整的功能,具有較好的錯誤處理。

HRESULT GetBitmap(LPTSTR pszFileName, BITMAPINFOHEADER** ppbmih)
{
    HRESULT hr;
    CComPtr<IMediaDet> pDet;
    hr = pDet.CoCreateInstance(__uuidof(MediaDet));
    if (FAILED(hr)) return hr;

    // Convert the file name to a BSTR.
    CComBSTR bstrFilename(pszFileName);
    hr = pDet->put_Filename(bstrFilename);
    if (FAILED(hr)) return hr;

    long lStreams;
    bool bFound = false;
    hr = pDet->get_OutputStreams(&lStreams);
    if (FAILED(hr)) return hr;

    for (long 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 *pVih = (VIDEOINFOHEADER*)(mt.pbFormat);
            width = pVih->bmiHeader.biWidth;
            height = pVih->bmiHeader.biHeight;
        
            // We want the absolute height, don't care about orientation.
            if (height < 0) height *= -1;
        }
        else
        {
            hr = VFW_E_INVALIDMEDIATYPE; // Should not happen, in theory.
        }
        FreeMediaType(mt);
    }
    if (FAILED(hr)) return hr;
    
    // Find the required buffer size.
    long size;
    hr = pDet->GetBitmapBits(0, &size, NULL, width, height);
    if (SUCCEEDED(hr)) 
    {
        char *pBuffer = new char[size];
        if (!pBuffer) return E_OUTOFMEMORY;
        try {
            hr = pDet->GetBitmapBits(0, NULL, pBuffer, width, height);
            if (SUCCEEDED(hr))
            {
                // Delete the old image, if any.
                if (*ppbmih) delete[] (*ppbmih);
                (*ppbmih) = (BITMAPINFOHEADER*)pBuffer;
            }
            else
            {
                delete [] pBuffer;
            }
        }
        catch (...) {
            delete [] pBuffer;
            return E_OUTOFMEMORY;
        }
    }
    return hr;
}

下一個步驟:第 4 步:在客戶區域上繪製位圖

抓取海報框