Поделиться через


Шаг 3. Реализация функции Frame-Grabbing

[Функция, связанная с этой страницей, DirectShow, является устаревшей функцией. Он был заменен MediaPlayer, IMFMediaEngineи аудио и видеозахват в Media Foundation. Эти функции оптимизированы для Windows 10 и Windows 11. Корпорация Майкрософт настоятельно рекомендует при написании нового кода использовать MediaPlayer, IMFMediaEngine и аудио-видеозахват в Media Foundation вместо использования DirectShow, по возможности. Корпорация Майкрософт предлагает, что существующий код, использующий устаревшие 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. Рисование растрового изображения в клиентской области

Захват кадра постера