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


Шаг 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. Получите рамку плаката, указав время поиска и целевое измерение.

Создайте объект Media Detector, вызвав CoCreateInstance:

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

Укажите имя файла с помощью метода IMediaDet::p ut_Filename . Этот метод принимает параметр BSTR .

hr = pDet->put_Filename(bstrFilename);

Получение количества потоков и циклическое перебирает каждый поток, проверяя тип носителя. Метод IMediaDet::get_OutputStreams извлекает количество потоков, а метод IMediaDet::p ut_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. Рисование растрового изображения в области клиента

Захват рамки плаката