Paso 3: Implementar la función Frame-Grabbing
[La característica asociada a esta página, DirectShow, es una característica heredada. Se ha reemplazado por MediaPlayer, IMFMediaEngine y Captura de audio/vídeo en Media Foundation. Esas características se han optimizado para Windows 10 y Windows 11. Microsoft recomienda encarecidamente que el nuevo código use MediaPlayer, IMFMediaEngine y Audio/Video Capture en Media Foundation en lugar de DirectShow, siempre que sea posible. Microsoft sugiere que el código existente que usa las API heredadas se reescriba para usar las nuevas API si es posible.
[Esta API no se admite y puede modificarse o no estar disponible en el futuro].
Este tema es el paso 3 de Agarrar un marco de póster.
El siguiente paso consiste en implementar la función GetBitmap, que usa el Detector de medios para obtener un marco de póster. Dentro de esta función, realice los pasos siguientes:
- Cree el Detector de medios.
- Especifique un archivo multimedia.
- Examine cada secuencia del archivo. Si es una secuencia de vídeo, obtenga las dimensiones nativas del vídeo.
- Obtenga un marco de póster, especificando el tiempo de búsqueda y la dimensión de destino.
Cree el objeto Media Detector llamando a CoCreateInstance:
CComPtr<IMediaDet> pDet;
hr = pDet.CoCreateInstance(__uuidof(MediaDet));
Especifique un nombre de archivo con el método IMediaDet::p ut_Filename . Este método toma un parámetro BSTR .
hr = pDet->put_Filename(bstrFilename);
Obtenga el número de secuencias y recorra cada secuencia comprobando el tipo de medio. El método IMediaDet::get_OutputStreams recupera el número de secuencias y el método IMediaDet::p ut_CurrentStream especifica la secuencia que se va a examinar. Salga del bucle en la primera secuencia 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;
Si no se encontró ninguna secuencia de vídeo, la función se cierra.
En el código anterior, el método IMediaDet::get_StreamType devuelve solo el GUID de tipo principal. Esto es cómodo si no es necesario examinar el tipo de medio completo. Sin embargo, para obtener las dimensiones de vídeo, es necesario examinar el bloque de formato, por lo que se necesita el tipo de medio completo. Puede recuperarlo llamando al método IMediaDet::get_StreamMediaType , que rellena una estructura de AM_MEDIA_TYPE . El Detector de medios convierte todas las secuencias de vídeo en formato sin comprimir, con un bloque 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);
El método get_StreamMediaType asigna el bloque de formato, que el autor de la llamada debe liberar. En este ejemplo se usa la función FreeMediaType de la biblioteca de clases base.
Ahora ya está listo para obtener el marco del póster. En primer lugar, llame al método IMediaDet::GetBitmapBits con un puntero NULL para el búfer:
long lSize;
hr = pDet->GetBitmapBits(0, &lSize, NULL, width, height);
Esta llamada devuelve el tamaño de búfer necesario en el parámetro lSize . El primer parámetro especifica el tiempo de flujo al que buscar; en este ejemplo se usa la hora cero. Para el ancho y alto, en este ejemplo se usan las dimensiones de vídeo nativas obtenidas anteriormente. Si especifica otros valores, el Detector de medios ajustará el mapa de bits para que coincida.
Si el método se realiza correctamente, asigne el búfer y llame a GetBitmapBits de nuevo:
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;
}
Esta es la función completa, con un control de errores algo mejor.
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;
}
Siguiente: Paso 4: Dibujar el mapa de bits en el área de cliente
Temas relacionados