Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
[O recurso associado a esta página, DirectShow, é um recurso herdado. Foi substituído por MediaPlayer, IMFMediaEnginee Audio/Video Capture in Media Foundation. Esses recursos foram otimizados para Windows 10 e Windows 11. A Microsoft recomenda vivamente que o novo código utilize 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.]
[Esta API não é suportada e pode ser alterada ou indisponível no futuro.]
O filtroSample Grabber é um filtro de transformação que pode ser usado para capturar amostras de mídia de um fluxo à medida que passam pelo gráfico de filtro.
Se você quiser simplesmente pegar um bitmap de um arquivo de vídeo, é mais fácil usar o Media Detetor (MediaDet) objeto. Consulte Pegando um quadro de pôster para obter detalhes. O Sample Grabber é mais flexível, no entanto, porque funciona com praticamente qualquer tipo de mídia (consulte ISampleGrabber::SetMediaType para as poucas exceções) e oferece mais controle para o aplicativo.
- Criar o Gerenciador de Gráficos de Filtro
- Adicione o capturador de amostras ao gráfico de filtro
- Definir o tipo de mídia
- criar o gráfico de filtro
- Executar o gráfico
- Agarre a amostra
- Exemplo de código
- Tópicos relacionados
Criar o Gerenciador de gráficos de filtro
Para começar, crie o Filter Graph Manager e consulte as interfaces IMediaControl e IMediaEventEx.
IGraphBuilder *pGraph = NULL;
IMediaControl *pControl = NULL;
IMediaEventEx *pEvent = NULL;
HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL,
CLSCTX_INPROC_SERVER,IID_PPV_ARGS(&pGraph));
if (FAILED(hr))
{
goto done;
}
hr = pGraph->QueryInterface(IID_PPV_ARGS(&pControl));
if (FAILED(hr))
{
goto done;
}
hr = pGraph->QueryInterface(IID_PPV_ARGS(&pEvent));
if (FAILED(hr))
{
goto done;
}
Adicionar o Sample Grabber ao gráfico de filtro
Crie uma instância do filtro Sample Grabber e adicione-a ao gráfico de filtro. Consulte o filtro Sample Grabber para a interface ISampleGrabber .
IBaseFilter *pGrabberF = NULL;
ISampleGrabber *pGrabber = NULL;
// Create the Sample Grabber filter.
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pGrabberF));
if (FAILED(hr))
{
goto done;
}
hr = pGraph->AddFilter(pGrabberF, L"Sample Grabber");
if (FAILED(hr))
{
goto done;
}
hr = pGrabberF->QueryInterface(IID_PPV_ARGS(&pGrabber));
if (FAILED(hr))
{
goto done;
}
Definir o tipo de mídia
Quando você cria o Sample Grabber pela primeira vez, ele não tem nenhum tipo de mídia preferido. Isso significa que você pode se conectar a praticamente qualquer filtro no gráfico, mas não teria controle sobre o tipo de dados recebidos. Antes de criar o resto do gráfico, portanto, você deve definir um tipo de mídia para o Sample Grabber, chamando o ISampleGrabber::SetMediaType método.
Quando o Sample Grabber se conectar, ele comparará esse tipo de mídia com o tipo de mídia oferecido pelo outro filtro. Os únicos campos que ele verifica são o tipo principal, subtipo e tipo de formato. Para qualquer um deles, o valor GUID_NULL significa "aceitar qualquer valor". Na maioria das vezes, você vai querer definir o tipo principal e subtipo. Por exemplo, o código a seguir especifica vídeo RGB de 24 bits não compactado:
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(mt));
mt.majortype = MEDIATYPE_Video;
mt.subtype = MEDIASUBTYPE_RGB24;
hr = pGrabber->SetMediaType(&mt);
if (FAILED(hr))
{
goto done;
}
Criar o gráfico de filtro
Agora você pode criar o resto do gráfico de filtro. Como o Sample Grabber só se conectará usando o tipo de mídia que você especificou, isso permite que você aproveite os mecanismos de Intelligent Connect do Filter Graph Manager ao criar o gráfico.
Por exemplo, se você especificou vídeo não compactado, você pode conectar um filtro de origem ao Sample Grabber, e o Filter Graph Manager adicionará automaticamente o analisador de arquivos e o decodificador. O exemplo a seguir usa a função auxiliar ConnectFilters, listada em Conectar dois filtros:
IBaseFilter *pSourceF = NULL;
IEnumPins *pEnum = NULL;
IPin *pPin = NULL;
hr = pGraph->AddSourceFilter(pszVideoFile, L"Source", &pSourceF);
if (FAILED(hr))
{
goto done;
}
hr = pSourceF->EnumPins(&pEnum);
if (FAILED(hr))
{
goto done;
}
while (S_OK == pEnum->Next(1, &pPin, NULL))
{
hr = ConnectFilters(pGraph, pPin, pGrabberF);
SafeRelease(&pPin);
if (SUCCEEDED(hr))
{
break;
}
}
if (FAILED(hr))
{
goto done;
}
O Sample Grabber é um filtro de transformação, portanto, o pino de saída deve ser conectado a outro filtro. Muitas vezes, você pode simplesmente querer descartar as amostras depois de terminar de usá-las. Nesse caso, conecte o Sample Grabber ao Null Renderer Filter, que descarta os dados recebidos.
O exemplo a seguir conecta o Sample Grabber ao filtro Null Renderer:
IBaseFilter *pNullF = NULL;
hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pNullF));
if (FAILED(hr))
{
goto done;
}
hr = pGraph->AddFilter(pNullF, L"Null Filter");
if (FAILED(hr))
{
goto done;
}
hr = ConnectFilters(pGraph, pGrabberF, pNullF);
if (FAILED(hr))
{
goto done;
}
Lembre-se de que colocar o Sample Grabber entre um decodificador de vídeo e o renderizador de vídeo pode prejudicar significativamente o desempenho de renderização. O Sample Grabber é um filtro trans-local, o que significa que o buffer de saída é o mesmo que o buffer de entrada. Para renderização de vídeo, é provável que o buffer de saída esteja localizado na placa gráfica, onde as operações de leitura são muito mais lentas, em comparação com as operações de leitura na memória principal.
Executar o gráfico
O Sample Grabber opera em um de dois modos:
- O modo de buffer faz uma cópia de cada amostra antes de entregar a amostra a jusante.
- O modo de retorno de chamada invoca uma função de retorno de chamada definida pela aplicação em cada amostra.
Este artigo descreve o modo de armazenamento em buffer. (Antes de usar o modo de retorno de chamada, esteja ciente de que a função de retorno de chamada deve ser bastante limitada. Caso contrário, pode reduzir drasticamente o desempenho ou até mesmo causar impasses. Para obter mais informações, consulte ISampleGrabber::SetCallback.) Para ativar o modo de buffer, chame o método ISampleGrabber::SetBufferSamples com o valor TRUE.
Opcionalmente, chame o método ISampleGrabber::SetOneShot com o valor TRUE. Isso faz com que o Sample Grabber pare depois de receber a primeira amostra de mídia, o que é útil se você quiser pegar um único quadro do fluxo. Procure o tempo desejado, execute o gráfico e aguarde o evento EC_COMPLETE. Observe que o nível de precisão do quadro depende da fonte. Por exemplo, procurar um ficheiro MPEG frequentemente não é preciso em termos de fotogramas.
Para executar o gráfico o mais rápido possível, desligue o relógio do gráfico conforme descrito em Definindo o relógio do gráfico.
O exemplo a seguir habilita o modo one-shot e o modo buffering, executa o gráfico de filtro e aguarda a conclusão.
hr = pGrabber->SetOneShot(TRUE);
if (FAILED(hr))
{
goto done;
}
hr = pGrabber->SetBufferSamples(TRUE);
if (FAILED(hr))
{
goto done;
}
hr = pControl->Run();
if (FAILED(hr))
{
goto done;
}
long evCode;
hr = pEvent->WaitForCompletion(INFINITE, &evCode);
Pegue a amostra
No modo de buffer, o Sample Grabber armazena uma cópia de cada amostra. O método ISampleGrabber::GetCurrentBuffer copia o buffer para uma matriz alocada pelo chamador. Para determinar o tamanho da matriz necessária, primeiro chame GetCurrentBuffer com um ponteiro NULL para o endereço da matriz. Em seguida, aloque a matriz e chame o método uma segunda vez para copiar o buffer. O exemplo a seguir mostra essas etapas.
// Find the required buffer size.
long cbBuffer;
hr = pGrabber->GetCurrentBuffer(&cbBuffer, NULL);
if (FAILED(hr))
{
goto done;
}
pBuffer = (BYTE*)CoTaskMemAlloc(cbBuffer);
if (!pBuffer)
{
hr = E_OUTOFMEMORY;
goto done;
}
hr = pGrabber->GetCurrentBuffer(&cbBuffer, (long*)pBuffer);
if (FAILED(hr))
{
goto done;
}
Você precisará saber o formato exato dos dados no buffer. Para obter essas informações, chame o ISampleGrabber::GetConnectedMediaType método. Este método preenche uma estrutura AM_MEDIA_TYPE com o formato.
Para um fluxo de vídeo não compactado, as informações de formato estão contidas numa estrutura VIDEOINFOHEADER. O exemplo a seguir mostra como obter as informações de formato para um fluxo de vídeo não compactado.
// Examine the format block.
if ((mt.formattype == FORMAT_VideoInfo) &&
(mt.cbFormat >= sizeof(VIDEOINFOHEADER)) &&
(mt.pbFormat != NULL))
{
VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)mt.pbFormat;
hr = WriteBitmap(pszBitmapFile, &pVih->bmiHeader,
mt.cbFormat - SIZE_PREHEADER, pBuffer, cbBuffer);
}
else
{
// Invalid format.
hr = VFW_E_INVALIDMEDIATYPE;
}
Observação
O Sample Grabber não suporta VIDEOINFOHEADER2.
Código de exemplo
Aqui está o código completo para os exemplos anteriores.
Observação
Este exemplo usa a função SafeRelease para liberar ponteiros de interface.
#include <windows.h>
#include <dshow.h>
#include "qedit.h"
template <class T> void SafeRelease(T **ppT)
{
if (*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
HRESULT WriteBitmap(PCWSTR, BITMAPINFOHEADER*, size_t, BYTE*, size_t);
HRESULT GrabVideoBitmap(PCWSTR pszVideoFile, PCWSTR pszBitmapFile)
{
IGraphBuilder *pGraph = NULL;
IMediaControl *pControl = NULL;
IMediaEventEx *pEvent = NULL;
IBaseFilter *pGrabberF = NULL;
ISampleGrabber *pGrabber = NULL;
IBaseFilter *pSourceF = NULL;
IEnumPins *pEnum = NULL;
IPin *pPin = NULL;
IBaseFilter *pNullF = NULL;
BYTE *pBuffer = NULL;
HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL,
CLSCTX_INPROC_SERVER,IID_PPV_ARGS(&pGraph));
if (FAILED(hr))
{
goto done;
}
hr = pGraph->QueryInterface(IID_PPV_ARGS(&pControl));
if (FAILED(hr))
{
goto done;
}
hr = pGraph->QueryInterface(IID_PPV_ARGS(&pEvent));
if (FAILED(hr))
{
goto done;
}
// Create the Sample Grabber filter.
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pGrabberF));
if (FAILED(hr))
{
goto done;
}
hr = pGraph->AddFilter(pGrabberF, L"Sample Grabber");
if (FAILED(hr))
{
goto done;
}
hr = pGrabberF->QueryInterface(IID_PPV_ARGS(&pGrabber));
if (FAILED(hr))
{
goto done;
}
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(mt));
mt.majortype = MEDIATYPE_Video;
mt.subtype = MEDIASUBTYPE_RGB24;
hr = pGrabber->SetMediaType(&mt);
if (FAILED(hr))
{
goto done;
}
hr = pGraph->AddSourceFilter(pszVideoFile, L"Source", &pSourceF);
if (FAILED(hr))
{
goto done;
}
hr = pSourceF->EnumPins(&pEnum);
if (FAILED(hr))
{
goto done;
}
while (S_OK == pEnum->Next(1, &pPin, NULL))
{
hr = ConnectFilters(pGraph, pPin, pGrabberF);
SafeRelease(&pPin);
if (SUCCEEDED(hr))
{
break;
}
}
if (FAILED(hr))
{
goto done;
}
hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pNullF));
if (FAILED(hr))
{
goto done;
}
hr = pGraph->AddFilter(pNullF, L"Null Filter");
if (FAILED(hr))
{
goto done;
}
hr = ConnectFilters(pGraph, pGrabberF, pNullF);
if (FAILED(hr))
{
goto done;
}
hr = pGrabber->SetOneShot(TRUE);
if (FAILED(hr))
{
goto done;
}
hr = pGrabber->SetBufferSamples(TRUE);
if (FAILED(hr))
{
goto done;
}
hr = pControl->Run();
if (FAILED(hr))
{
goto done;
}
long evCode;
hr = pEvent->WaitForCompletion(INFINITE, &evCode);
// Find the required buffer size.
long cbBuffer;
hr = pGrabber->GetCurrentBuffer(&cbBuffer, NULL);
if (FAILED(hr))
{
goto done;
}
pBuffer = (BYTE*)CoTaskMemAlloc(cbBuffer);
if (!pBuffer)
{
hr = E_OUTOFMEMORY;
goto done;
}
hr = pGrabber->GetCurrentBuffer(&cbBuffer, (long*)pBuffer);
if (FAILED(hr))
{
goto done;
}
hr = pGrabber->GetConnectedMediaType(&mt);
if (FAILED(hr))
{
goto done;
}
// Examine the format block.
if ((mt.formattype == FORMAT_VideoInfo) &&
(mt.cbFormat >= sizeof(VIDEOINFOHEADER)) &&
(mt.pbFormat != NULL))
{
VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)mt.pbFormat;
hr = WriteBitmap(pszBitmapFile, &pVih->bmiHeader,
mt.cbFormat - SIZE_PREHEADER, pBuffer, cbBuffer);
}
else
{
// Invalid format.
hr = VFW_E_INVALIDMEDIATYPE;
}
FreeMediaType(mt);
done:
CoTaskMemFree(pBuffer);
SafeRelease(&pPin);
SafeRelease(&pEnum);
SafeRelease(&pNullF);
SafeRelease(&pSourceF);
SafeRelease(&pGrabber);
SafeRelease(&pGrabberF);
SafeRelease(&pControl);
SafeRelease(&pEvent);
SafeRelease(&pGraph);
return hr;
};
// Writes a bitmap file
// pszFileName: Output file name.
// pBMI: Bitmap format information (including pallete).
// cbBMI: Size of the BITMAPINFOHEADER, including palette, if present.
// pData: Pointer to the bitmap bits.
// cbData Size of the bitmap, in bytes.
HRESULT WriteBitmap(PCWSTR pszFileName, BITMAPINFOHEADER *pBMI, size_t cbBMI,
BYTE *pData, size_t cbData)
{
HANDLE hFile = CreateFile(pszFileName, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, 0, NULL);
if (hFile == NULL)
{
return HRESULT_FROM_WIN32(GetLastError());
}
BITMAPFILEHEADER bmf = { };
bmf.bfType = 'MB';
bmf.bfSize = cbBMI+ cbData + sizeof(bmf);
bmf.bfOffBits = sizeof(bmf) + cbBMI;
DWORD cbWritten = 0;
BOOL result = WriteFile(hFile, &bmf, sizeof(bmf), &cbWritten, NULL);
if (result)
{
result = WriteFile(hFile, pBMI, cbBMI, &cbWritten, NULL);
}
if (result)
{
result = WriteFile(hFile, pData, cbData, &cbWritten, NULL);
}
HRESULT hr = result ? S_OK : HRESULT_FROM_WIN32(GetLastError());
CloseHandle(hFile);
return hr;
}
Tópicos relacionados