Media Foundation — Как изменить размер кадра в MFT (Media Foundation Transform)

Я пытаюсь реализовать MFT, который может вращать видео. Сам поворот будет выполняться внутри функции преобразования. Для этого мне нужно изменить размер выходного кадра, но я не знаю, как это сделать.

В качестве отправной точки я использовал пример MFT_Grayscale, данный Microsoft. Я включил этот MFT в частичную топологию в качестве узла преобразования

HRESULT Player::AddBranchToPartialTopology(
IMFTopology *pTopology,
IMFPresentationDescriptor *pSourcePD,
DWORD iStream
)
{
...
IMFTopologyNode pTransformNode = NULL;
...
hr = CreateTransformNode(CLSID_GrayscaleMFT, &pTransformNode);
...
hr = pSourceNode->ConnectOutput(0, pTransformNode, 0);
hr = pTransformNode->ConnectOutput(0, pOutputNode, 0);
...
}

Этот код работает до сих пор. MFT в градациях серого применяется и работает как положено. В любом случае, я хочу изменить этот mft для обработки вращения видео. Итак, давайте предположим, что я хочу повернуть видео на 90 градусов. Для этого необходимо изменить ширину и высоту моего входного кадра. Я пробовал разные вещи, но ни одна из них не работает так, как ожидалось.
Основано на первом комментарии в этой теме Как изменить размер выходного кадра (видео) Media Foundation Transform? я начал менять реализацию SetOutputType. я вызвал GetAttributeSize внутри GetOutputType, чтобы получить фактический размер кадра. Сбой при попытке установить новый frame_size (при запуске воспроизведения я получаю hresult 0xc00d36b4 (указанные данные недействительны, несовместимы или не поддерживаются этим объектом)

HRESULT CGrayscale::SetOutputType(
DWORD           dwOutputStreamID,
IMFMediaType    *pType, // Can be NULL to clear the output type.
DWORD           dwFlags
)
{ ....
//Receive the actual frame_size of pType (works as expected)
hr = MFGetAttributeSize(
pType,
MF_MT_FRAME_SIZE,
&width,
&height
));
...
//change the framesize
hr = MFSetAttributeSize(
pType,
MF_MT_FRAME_SIZE,
height,
width
));
}

Я уверен, что я что-то здесь упускаю, поэтому любая подсказка будет принята с благодарностью.

заранее спасибо

5

Решение

Eсть преобразование доступно в W8 +, который должен делать вращение. Мне не очень повезло с этим, но, вероятно, это можно заставить работать. Я собираюсь предположить, что это не жизнеспособное решение для вас.

Более интересный случай — создание MFT для преобразования.

Оказывается, есть несколько шагов, чтобы превратить «Оттенки серого» в ротатор.

1) Как вы и догадались, вам нужно повлиять на размер кадра на тип вывода. Однако изменение типа, передаваемого в SetOutputType, является просто неправильным. Тип pType, отправляемый в SetOutputType, является типом, который клиент просит вас поддерживать. Изменение этого типа носителя на что-то Другой чем то, что они просили, тогда возвращение S_OK, чтобы сказать, что вы поддерживаете, не имеет смысла.

Вместо этого вам нужно изменить значение, отправленное обратно из GetOutputAvailableType.

2) При вычислении типа для отправки обратно из GetOutputAvailableType вам необходимо основывать его на IMFMediaType, который клиент отправил в SetInputType, с некоторыми изменениями. И да, вы хотите настроить MF_MT_FRAME_SIZE, но вам, вероятно, также необходимо настроить MF_MT_DEFAULT_STRIDE, MF_MT_GEOMETRIC_APERTURE и (возможно) MF_MT_MINIMUM_DISPLAY_APERTURE. Возможно, вам может понадобиться настроить MF_MT_SAMPLE_SIZE.

3) Вы не сказали, планировали ли вы количество вращения в начале потока или что-то, что меняется во время игры. Когда я писал это, я использовал IMFAttributes, возвращенные из IMFTransform :: GetAttributes, чтобы указать вращение. Перед обработкой каждого кадра читается текущее значение. Чтобы это работало правильно, вы должны иметь возможность отправить MF_E_TRANSFORM_STREAM_CHANGE обратно из OnProcessOutput.

4) Будучи ленивым, я не хотел выяснять, как вращать NV12 или YUY2 или что-то подобное. Но есть функции, легко доступные для этого для RGB32. Поэтому, когда вызывается мой GetInputAvailableType, я запрашиваю RGB32.

Я экспериментировал с поддержкой других типов ввода, таких как RGB24, RGB565 и т. Д., Но столкнулся с проблемой. Когда ваш тип вывода — RGB24, MF добавляет еще один MFT вниз, чтобы преобразовать RGB24 обратно во что-то, что он может более легко использовать (возможно, RGB32). И это MFT не Поддержка смены типов медиа в среднем потоке. Я смог заставить это работать, принимая множество подтипов для ввода, но всегда выводя RGB32, повернутый как указано.

Это звучит сложно, но в основном это не так. Если вы прочитаете код, вы, вероятно, скажете: «О, я понял». Я бы предложил вам свой исходный код, но я не уверен, насколько он будет полезен для вас. Это в c #, а вы спрашивали о c ++.

С другой стороны, я делаю шаблон для облегчения написания MFT. ~ Десяток строк кода на C # для создания максимально простого MFT. MFT вращения c # составляет ~ 131 строки, как подсчитано в метриках VS Analyze / Calculate code (исключая шаблон). Я экспериментирую с версией c ++, но она все еще немного грубая.

Я что-то забыл? Наверное, куча вещей. Например, не забудьте сгенерировать новый Guid для вашего MFT вместо использования оттенков серого. Но я думаю, что я достиг вершин.

Изменить: Теперь, когда моя версия шаблона на С ++ начинает работать, я чувствую себя комфортно, публикуя некоторый реальный код. Это может сделать некоторые из пунктов выше более ясными. Например, в # 2, я говорю о том, как основывать тип вывода на типе ввода. Вы можете видеть, что происходит в CreateOutputFromInput. И фактический код поворота находится в WriteIt ().

Я немного упростил код для размера, но, надеюсь, это заставит вас сказать: «О, я понял».

void OnProcessSample(IMFSample *pSample, bool Discontinuity, int InputMessageNumber)
{
HRESULT hr = S_OK;

int i = MFGetAttributeUINT32(GetAttributes(), AttribRotate, 0);
i &= 7;

// Will the output use different dimensions than the input?
bool IsOdd = (i & 1) == 1;

// Does the current AttribRotate rotation give a different
// orientation than the old one?
if (IsOdd != m_WasOdd)
{
// Yes, change the output type.
OutputSample(NULL, InputMessageNumber);
m_WasOdd = IsOdd;
}

// Process it.
DoWork(pSample, (RotateFlipType)i);

// Send the modified input sample to the output sample queue.
OutputSample(pSample, InputMessageNumber);
}

void OnSetInputType()
{
HRESULT hr = S_OK;

m_imageWidthInPixels = 0;
m_imageHeightInPixels = 0;
m_cbImageSize = 0;
m_lInputStride = 0;

IMFMediaType *pmt = GetInputType();

// type can be null to clear
if (pmt != NULL)
{
hr = MFGetAttributeSize(pmt, MF_MT_FRAME_SIZE, &m_imageWidthInPixels, &m_imageHeightInPixels);
ThrowExceptionForHR(hr);

hr = pmt->GetUINT32(MF_MT_DEFAULT_STRIDE, &m_lInputStride);
ThrowExceptionForHR(hr);

// Calculate the image size (not including padding)
m_cbImageSize = m_imageHeightInPixels * m_lInputStride;
}
else
{
// Since the input must be set before the output, nulling the
// input must also clear the output.  Note that nulling the
// input is only valid if we are not actively streaming.

SetOutputType(NULL);
}
}

IMFMediaType *CreateOutputFromInput(IMFMediaType *inType)
{
// For some MFTs, the output type is the same as the input type.
// However, since we are rotating, several attributes in the
// media type (like frame size) must be different on our output.
// This routine generates the appropriate output type for the
// current input type, given the current state of m_WasOdd.

IMFMediaType *pOutputType = CloneMediaType(inType);

if (m_WasOdd)
{
HRESULT hr;
UINT32 h, w;

// Intentionally backward
hr = MFGetAttributeSize(inType, MF_MT_FRAME_SIZE, &h, &w);
ThrowExceptionForHR(hr);

hr = MFSetAttributeSize(pOutputType, MF_MT_FRAME_SIZE, w, h);
ThrowExceptionForHR(hr);

MFVideoArea *a = GetArea(inType, MF_MT_GEOMETRIC_APERTURE);
if (a != NULL)
{
a->Area.cy = h;
a->Area.cx = w;
SetArea(pOutputType, MF_MT_GEOMETRIC_APERTURE, a);
}

a = GetArea(inType, MF_MT_MINIMUM_DISPLAY_APERTURE);
if (a != NULL)
{
a->Area.cy = h;
a->Area.cx = w;
SetArea(pOutputType, MF_MT_MINIMUM_DISPLAY_APERTURE, a);
}

hr = pOutputType->SetUINT32(MF_MT_DEFAULT_STRIDE, w * 4);
ThrowExceptionForHR(hr);
}

return pOutputType;
}

void WriteIt(BYTE *pBuffer, RotateFlipType fm)
{
Bitmap *v = new Bitmap((int)m_imageWidthInPixels, (int)m_imageHeightInPixels, (int)m_lInputStride, PixelFormat32bppRGB, pBuffer);
if (v == NULL)
throw (HRESULT)E_OUTOFMEMORY;

try
{
Status s;

s = v->RotateFlip(fm);
if (s != Ok)
throw (HRESULT)E_UNEXPECTED;

Rect r;

if (!m_WasOdd)
{
r.Width = (int)m_imageWidthInPixels;
r.Height = (int)m_imageHeightInPixels;
}
else
{
r.Height = (int)m_imageWidthInPixels;
r.Width = (int)m_imageHeightInPixels;
}

BitmapData bmd;
bmd.Width = r.Width,
bmd.Height = r.Height,
bmd.Stride = 4*bmd.Width;
bmd.PixelFormat = PixelFormat32bppARGB;
bmd.Scan0 = (VOID*)pBuffer;
bmd.Reserved = NULL;

s = v->LockBits(&r, ImageLockModeRead + ImageLockModeUserInputBuf, PixelFormat32bppRGB, &bmd);
if (s != Ok)
throw (HRESULT)E_UNEXPECTED;

s = v->UnlockBits(&bmd);
if (s != Ok)
throw (HRESULT)E_UNEXPECTED;
}
catch(...)
{
delete v;
throw;
}

delete v;
}
2

Другие решения


По вопросам рекламы [email protected]