Windows Media Foundation использует IMFTransform для декодирования кадров фильмов mp4 в 2D текстуры
Я пытаюсь декодировать видео mp4 с помощью классов Windows Media Foundation и преобразования кадров в 2D-текстуры, которые могут быть использованы шейдером DirectX для рендеринга. Я смог прочитать исходный поток с помощью MFCreateSourceReaderfromURL и смог прочитать тип носителя потока, который имеет свой основной тип MFMEdiaType_Video и минорный тип как MFVideoFormat_H264, как и ожидалось.
Теперь мне нужно преобразовать этот формат в формат RGB, который может использоваться для инициализации ресурса D3D11_TEXTURE2D и представления ресурсов, которые затем могут быть переданы в пиксельный шейдер HLSL для выборки. Я устал использовать класс IMFTransform , чтобы сделать преобразование за меня, но когда я пытаюсь установить тип вывода на преобразование в любой вариант MFVideoFormat_RGB, я получаю ошибку. Я также попытался установить новый тип вывода на исходном считывателе и просто выборку, надеясь получить образец в правильном формате, но снова мне не повезло.
Итак, мой вопросы будут:
-
Возможно ли такое преобразование?
-
Можно ли это сделать с помощью классов IMFTransform/SourceReader, как я устал выше, и мне просто нужно настроить код или мне нужно сделать этот тип преобразования вручную?
-
Является ли это лучшим способом передачи данных текстур видео в шейдер для выборки или есть более простая альтернатива, о которой я не думал.
Используемая ОС является Windows 7, поэтому я не могу использовать интерфейс SourceReaderEx или ID3D11VideoDevice, потому что, насколько мне известно, эти решения доступны только в Windows 8.
Любая помощь / указатели в правильном направлении были бы очень признательны, я также могу предоставить некоторый исходный код, если это необходимо.
3 ответа:
Я вижу, что у вас есть некоторая ошибка в понимании медиа-Фонда. Вы хотите получить изображение в формате RGB от MFVideoFormat_H264, но вы не используете декодер H264. Вы написали: "Я устал использовать класс IMFTransform" - IMFTransform-это не класс. Это интерфейс для преобразования COM-объектов. Необходимо создать декодер com object Media Foundation H264. CLSID для декодера Microsoft software H264 - это CLSID_CMSH264DecoderMFT. Однако из этого декодера вы можете получить выходное изображение в следующем форматы: Типы Вывода
MFVideoFormat_I420
MFVideoFormat_IYUV
MFVideoFormat_NV12
MFVideoFormat_YUY2
MFVideoFormat_YV12
Вы можете создать D3D11_TEXTURE2D из одного из них. Или вы можете сделать что-то вроде этого из моего проекта CaptureManager SDK:
CComPtrCustom<IMFTransform> lColorConvert; if (!Result(lColorConvert.CoCreateInstance(__uuidof(CColorConvertDMO)))) { lresult = MediaFoundationManager::setInputType( lColorConvert, 0, lVideoMediaType, 0); if (lresult) { break; } DWORD lTypeIndex = 0; while (!lresult) { CComPtrCustom<IMFMediaType> lOutputType; lresult = lColorConvert->GetOutputAvailableType(0, lTypeIndex++, &lOutputType); if (!lresult) { lresult = MediaFoundationManager::getGUID( lOutputType, MF_MT_SUBTYPE, lSubType); if (lresult) { break; } if (lSubType == MFVideoFormat_RGB32) { LONG lstride = 0; MediaFoundationManager::getStrideForBitmapInfoHeader( lSubType, lWidth, lstride); if (lstride < 0) lstride = -lstride; lBitRate = (lHight * (UINT32)lstride * 8 * lNumerator) / lDenominator; lresult = MediaFoundationManager::setUINT32( lOutputType, MF_MT_AVG_BITRATE, lBitRate); if (lresult) { break; } PROPVARIANT lVarItem; lresult = MediaFoundationManager::getItem( *aPtrPtrInputMediaType, MF_MT_FRAME_RATE, lVarItem); if (lresult) { break; } lresult = MediaFoundationManager::setItem( lOutputType, MF_MT_FRAME_RATE, lVarItem); if (lresult) { break; } (*aPtrPtrInputMediaType)->Release(); *aPtrPtrInputMediaType = lOutputType.detach(); break; } } } }
Вы можете установить ColorConvertDMO для преобразования из выходного формата декодера H264 в нужный вам.
Кроме того, вы можете просмотреть код по ссылке: videoInput. Этот код берет живое видео с веб-камеры и декодирует его в RGB. Если вы замените источник веб-камеры на источник видеофайла mp4, вы получите решение, которое близко к вашим потребностям.
С уважением
Возможен ли такой тип преобразования?
Да, это возможно. Stock H. 264 Video Decoder MFT является "Direct3D aware", что означает, что он может декодировать видео в поверхности Direct3D 9/текстуры Direct3D 11, используя DXVA. Или, если аппаратные возможности недостаточны, есть также резервный режим программного обеспечения. Вы заинтересованы в том, чтобы выходные данные доставлялись прямо в текстуру из соображений производительности (в противном случае вам пришлось бы загружать эти данные самостоятельно, тратя процессор и видео ресурсы на это).
Можно ли это сделать с помощью классов IMFTransform/SourceReader, как я устал выше, и мне просто нужно настроить код или мне нужно сделать этот тип преобразования вручную?
IMFTransform
это абстрактный интерфейс. Он реализован декодером H. 264 (а также другими MFTs), и вы можете использовать его напрямую, или вы можете использовать API более высокого уровня Source Reader, чтобы заставить его управлять чтением видео из файла и декодированием с помощью этого MFT.То есть MFT и Source Reader на самом деле не является эксклюзивным альтернативным вариантом, а вместо этого API более высокого и более низкого уровня. Интерфейс MFT предлагается декодером, и вы несете ответственность за подачу H. 264 и слив декодированного вывода. Source Reader управляет тем же MFT и добавляет возможность чтения файлов.
Сам Source Reader доступен в Windows 7, кстати (даже на Vista, может быть ограничен в наборе функций по сравнению с более новыми операционными системами).
Декодирование может быть выполнено следующим кодом:
MFT_OUTPUT_DATA_BUFFER loutputDataBuffer; initOutputDataBuffer( lTransform, loutputDataBuffer); DWORD lprocessOutputStatus = 0; lresult = lTransform->ProcessOutput( 0, 1, &loutputDataBuffer, &lprocessOutputStatus); if ((HRESULT)lresult == E_FAIL) { break; }
Функция initOutputDataBuffer выделяет необходимую память. Пример этой функции представлен здесь:
Result initOutputDataBuffer(IMFTransform* aPtrTransform, MFT_OUTPUT_DATA_BUFFER& aRefOutputBuffer) { Result lresult; MFT_OUTPUT_STREAM_INFO loutputStreamInfo; DWORD loutputStreamId = 0; CComPtrCustom<IMFSample> lOutputSample; CComPtrCustom<IMFMediaBuffer> lMediaBuffer; do { if (aPtrTransform == nullptr) { lresult = E_POINTER; break; } ZeroMemory(&loutputStreamInfo, sizeof(loutputStreamInfo)); ZeroMemory(&aRefOutputBuffer, sizeof(aRefOutputBuffer)); lresult = aPtrTransform->GetOutputStreamInfo(loutputStreamId, &loutputStreamInfo); if (lresult) { break; } if ((loutputStreamInfo.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) == 0 && (loutputStreamInfo.dwFlags & MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES) == 0) { lresult = MFCreateSample(&lOutputSample); if (lresult) { break; } lresult = MFCreateMemoryBuffer(loutputStreamInfo.cbSize, &lMediaBuffer); if (lresult) { break; } lresult = lOutputSample->AddBuffer(lMediaBuffer); if (lresult) { break; } aRefOutputBuffer.pSample = lOutputSample.Detach(); } else { lresult = S_OK; } aRefOutputBuffer.dwStreamID = loutputStreamId; } while (false); return lresult; }
Он должен получить информацию о выходных образцах с помощью метода GetOutputStreamInfo IMFTransform. MFT_OUTPUT_STREAM_INFO содержит информацию о необходимом размере памяти для вывода образца носителя-cbSize. Ему нужно выделить память с таким размером, добавить ее в MediaSample и присоединить к th MFT_OUTPUT_DATA_BUFFER.
Таким образом, вы видите, что написание кода для кодирования и декодирования видео с помощью прямого вызова функции MediaFoundation может быть сложным и требует значительных знаний о нем. Из описания вашей задачи я вижу, что вам нужно только расшифровать видео и представить его. Я могу посоветовать вам попробовать использовать функциональность сеанса Media Foundation. Он разработан инженерами Microsoft и уже включает в себя алгоритмы использования необходимых кодеров и оптимизирован. В проектах Сеанс Media Foundation используется для поиска подходящего декодера для Медиаисточника, который создается для веб-камеры и захвата кадров в несжатом формате. Он уже делает необходимую обработку. Вам нужно только заменить источник Мультимедиа из веб-камеры на источник Мультимедиа из видеофайла. Это может быть проще, чем написание кода с прямым вызовом IMFTransform для декодирования и позволяет упростить многие задачи (например - стабилизацию частоты кадров. Если код будет рендеринг изображения сразу после декодирования, а затем декодировать новый кадр, то он может рендерить 1 минутный видеоклип в течение нескольких секунд, или если рендеринг видео и другого контента может занять более одного кадра длительность видео может быть представлена в стиле "замедленной съемки" и рендеринг 1 минутного видеоклипа может занять 2, 3 или 5 минут. Я не знаю, для какого проекта вам нужно декодирование видео, но у вас должны быть серьезные основания для использования кода с прямым вызовом функций Media Foundation и межфазные границы.С уважением.