Etapa 3: Implementar a função Frame-Grabbing

[O recurso associado a esta página, DirectShow, é um recurso herdado. Ele foi substituído por MediaPlayer, IMFMediaEngine e Captura de Áudio/Vídeo na Media Foundation. Esses recursos foram otimizados para Windows 10 e Windows 11. A Microsoft recomenda fortemente que o novo código use MediaPlayer, IMFMediaEngine e Captura de Áudio/Vídeo no Media Foundation em vez de DirectShow, quando possível. A Microsoft sugere que o código existente que usa as APIs herdadas seja reescrito para usar as novas APIs, se possível.]

[Essa API não tem suporte e pode ser alterada ou indisponível no futuro.]

Este tópico é a Etapa 3 de Como pegar um quadro de pôster.

A próxima etapa é implementar a função GetBitmap, que usa o Detector de Mídia para pegar um quadro de pôster. Dentro dessa função, execute as seguintes etapas:

  1. Crie o Detector de Mídia.
  2. Especifique um arquivo de mídia.
  3. Examine cada fluxo no arquivo. Se for um fluxo de vídeo, obtenha as dimensões nativas do vídeo.
  4. Obtenha um quadro de pôster, especificando o tempo de busca e a dimensão de destino.

Crie o objeto Detector de Mídia chamando CoCreateInstance:

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

Especifique um nome de arquivo usando o método IMediaDet::p ut_Filename . Esse método usa um parâmetro BSTR .

hr = pDet->put_Filename(bstrFilename);

Obtenha o número de fluxos e faça um loop em cada fluxo verificando o tipo de mídia. O método IMediaDet::get_OutputStreams recupera o número de fluxos e o método IMediaDet::p ut_CurrentStream especifica qual fluxo examinar. Saia do loop no primeiro fluxo de vídeo.

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;

Se nenhum fluxo de vídeo for encontrado, a função será encerrada.

No código anterior, o método IMediaDet::get_StreamType retorna apenas o GUID do tipo principal. Isso será conveniente se você não precisar examinar o tipo de mídia completo. No entanto, para obter as dimensões de vídeo, é necessário examinar o bloco de formato, para que o tipo de mídia completo seja necessário. Você pode recuperá-lo chamando o método IMediaDet::get_StreamMediaType , que preenche uma estrutura AM_MEDIA_TYPE . O Detector de Mídia converte todos os fluxos de vídeo em formato descompactado, com um bloco de formato 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);

O método get_StreamMediaType aloca o bloco de formato, que o chamador deve liberar. Este exemplo usa a função FreeMediaType da biblioteca de classes base.

Agora você está pronto para obter o quadro de pôster. Primeiro chame o método IMediaDet::GetBitmapBits com um ponteiro NULL para o buffer:

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

Essa chamada retorna o tamanho do buffer necessário no parâmetro lSize . O primeiro parâmetro especifica o tempo de fluxo a ser buscado; este exemplo usa o tempo zero. Para a largura e a altura, este exemplo usa as dimensões de vídeo nativas obtidas anteriormente. Se você especificar outros valores, o Detector de Mídia esticará o bitmap para corresponder.

Se o método for bem-sucedido, aloque o buffer e chame GetBitmapBits novamente:

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;
}

Aqui está a função completa, com um tratamento de erro um pouco melhor.

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;
}

Próximo: Etapa 4: Desenhar o Bitmap na Área do Cliente

Pegando um quadro de pôster