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:
- Crie o Detector de Mídia.
- Especifique um arquivo de mídia.
- Examine cada fluxo no arquivo. Se for um fluxo de vídeo, obtenha as dimensões nativas do vídeo.
- 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
Tópicos relacionados