теперь мой вопрос: когда я впервые получаю данные и ProcessInput, все в порядке. но тогда я получу небольшие данные (может быть, всего 3 КБ?), И изображение будет неправильным и чего-то будет не хватать (только часть всего правильного изображения).
поэтому я думаю, что он не получает все данные, необходимые для IMFTransform, поэтому я буду хранить данные до тех пор, пока они не достигнут 30 КБ (или другого размера), а затем перейду к MFCreateMemoryBuffer.
да, теперь кадр изображения правильный до последнего кадра.
так что, наверное, я не получаю полные данные одного кадра, верно? как получить правильный размер одного полностью IMFSample(Frame)?
Я делаю как код @Simon Mourier, но есть изображение ошибки.
@SimonMourier, возможно, я неправильно спрашиваю. Я хочу спросить, какой размер (мин) данных буфера. h264, как вы знаете, сжат. и если я ничего не изменил (рабочий стол не изменился и мышь не переместилась), кадр изображения такой же, как и перед кадром. данных не будет, да? Если я наведу курсор мыши очень сильно, размер данных h264 будет очень маленьким. и я получу его, а затем передам в ProcessInput в качестве параметра, и я не смогу получить правильный выходной кадр, пока не подожду еще данных
Итак, если вы декодируете кадры H264, вам не нужно обрабатывать входные выборки определенного размера (это сжатые входные данные), просто подайте фрагменты имеющихся у вас данных и убедитесь, что вы обрабатываете HRESULT из ProcessInput (например, продолжайте, если MF_E_NOTACCEPTING ) и ProcessOutput (например: продолжить, если MF_E_TRANSFORM_NEED_MORE_INPUT) осторожно.
спасибо @SimonMourier. как вы сказали, следует ли мне добавлять данные в буфер первого образца, когдаprocessOutputResult == MF_E_TRANSFORM_NEED_MORE_INPUT?
Да, для каждого фрагмента данных (то есть случайного количества байтов) создайте одну выборку с одним буфером с байтами, пока вы получаете это MF_E_TRANSFORM_NEED_MORE_INPUT.
пока я получаю это MF_E_TRANSFORM_NEED_MORE_INPUT, я передаю данные в один образец и один буфер, и мне не нужно создавать больше образцов. я правильно понимаю? и MF_E_TRANSFORM_NEED_MORE_INTPUT снова не будет
Если хотите, могу предоставить пример кода на C++.
@SimonMourier, вы сказали, создайте один образец с одним буфером. вы имеете в виду, что мне нужно создать один образец с одним буфером, когда я получаю MF_E_TRANSFORM_NEED_MORE_INPUT, а затем ProcessInput? или просто создать IMediaBuffer для AddBuffer к первому образцу?
@SimonMourier Я добавляю буфер (MFCreatememoryBuffer) в первый IMFSample, и там ничего не изменилось MF_E_TRANSFORM_NEED_MORE_INPUT





Вот пример кода, который декодирует поток байтов H264 с помощью преобразования Media Foundation H.264 Video Decoder.
Он имитирует поток кусков байтов различного размера с использованием файла, но принципы те же.
Ключевые моменты:
#include <windows.h>
#include <atlbase.h>
#include <mfapi.h>
#include <mferror.h>
#include <mfidl.h>
#include <mftransform.h>
#include <cstdlib>
#pragma comment(lib, "mfplat.lib")
#pragma comment(lib, "mfuuid.lib")
#define HRCHECK(__expr) {auto __hr=(__expr);if (FAILED(__hr)){wprintf(L"FAILURE 0x%08X (%i)\n\tline: %u file: '%s'\n\texpr: '" _CRT_WIDE(#__expr) L"'\n",__hr,__hr,__LINE__,_CRT_WIDE(__FILE__));_CrtDbgBreak();}}
#define WIN32CHECK(__expr) {if (!(__expr)){auto __hr=HRESULT_FROM_WIN32(GetLastError());{wprintf(L"FAILURE 0x%08X (%i)\n\tline: %u file: '%s'\n\texpr: '" _CRT_WIDE(#__expr) L"'\n",__hr,__hr,__LINE__,_CRT_WIDE(__FILE__));_CrtDbgBreak();}}}
static HRESULT SetOutputType(IMFTransform* transform, GUID format)
{
DWORD index = 0;
do
{
CComPtr<IMFMediaType> outputType;
auto hr = transform->GetOutputAvailableType(0, index++, &outputType);
if (FAILED(hr))
return hr;
GUID guid;
if (SUCCEEDED(outputType->GetGUID(MF_MT_SUBTYPE, &guid)) && guid == format)
{
HRCHECK(transform->SetOutputType(0, outputType, 0));
return S_OK;
}
} while (true);
}
int main()
{
HRCHECK(CoInitialize(nullptr));
{
HRCHECK(MFStartup(MF_VERSION));
// open file
auto file = CreateFile(L"h264.h264", GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, nullptr);
WIN32CHECK(file != INVALID_HANDLE_VALUE);
// create H264 transform https://learn.microsoft.com/en-us/windows/win32/medfound/h-264-video-decoder
CComPtr<IMFTransform> decoder;
HRCHECK(decoder.CoCreateInstance(CLSID_MSH264DecoderMFT));
// You can check in decoder attributes that MF_MT_FIXED_SIZE_SAMPLES is set to TRUE.
// Calling GetOutputStreamInfo this will tell you the MFT cannot provide samples
// as MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES and MFT_OUTPUT_STREAM_PROVIDES_SAMPLES are not set
// So we don't know enough information yet, we'll feed input samples until we get MF_E_TRANSFORM_STREAM_CHANGE and then we'll provide a sample as per doc:
// "If the input type contains only these two attributes, the decoder will offer a default output type, which acts as a placeholder."
// "When the decoder receives enough input samples to produce an output frame, it signals a format change by returning MF_E_TRANSFORM_STREAM_CHANGE"
UINT32 sampleSize = 0;
// input type is H264
CComPtr<IMFMediaType> inputType;
HRCHECK(MFCreateMediaType(&inputType));
HRCHECK(inputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
HRCHECK(inputType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264));
HRCHECK(decoder->SetInputType(0, inputType, 0)); // video is id 0
// get (and set) NV12 output type (could be I420, IYUV, YUY2, YV12)
HRCHECK(SetOutputType(decoder, MFVideoFormat_NV12));
do
{
// get a random chunk size between 500 and 1500
DWORD chunkSize = 500 + (1000 * (RAND_MAX - std::rand())) / RAND_MAX;
// create an MF input buffer & read into it
CComPtr<IMFMediaBuffer> inputBuffer;
HRCHECK(MFCreateMemoryBuffer(chunkSize, &inputBuffer));
BYTE* chunk;
HRCHECK(inputBuffer->Lock(&chunk, nullptr, nullptr));
DWORD read;
WIN32CHECK(ReadFile(file, chunk, chunkSize, &read, nullptr));
HRCHECK(inputBuffer->SetCurrentLength(read));
HRCHECK(inputBuffer->Unlock());
if (read)
{
CComPtr<IMFSample> inputSample;
HRCHECK(MFCreateSample(&inputSample));
HRCHECK(inputSample->AddBuffer(inputBuffer));
auto hr = decoder->ProcessInput(0, inputSample, 0);
if (hr != MF_E_NOTACCEPTING) // just go on
{
HRCHECK(hr);
}
}
else
{
// end of file, ask decoder to process all data from previous calls
HRCHECK(decoder->ProcessMessage(MFT_MESSAGE_COMMAND_DRAIN, 0));
}
CComPtr<IMFSample> outputSample;
HRCHECK(MFCreateSample(&outputSample));
MFT_OUTPUT_DATA_BUFFER outputBuffer{};
outputBuffer.pSample = outputSample;
if (sampleSize)
{
// now we know the size so we can (and must) allocate the MF output buffer
CComPtr<IMFMediaBuffer> outputBuffer;
HRCHECK(MFCreateMemoryBuffer(sampleSize, &outputBuffer));
HRCHECK(outputSample->AddBuffer(outputBuffer));
} // else just continue to process
DWORD status = 0;
auto hr = decoder->ProcessOutput(0, 1, &outputBuffer, &status);
if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) // just go on
{
if (!read) // file is all read
break;
continue;
}
// https://learn.microsoft.com/en-us/windows/win32/medfound/handling-stream-changes
if (hr == MF_E_TRANSFORM_STREAM_CHANGE)
{
// get (and set) NV12 output type (could be I420, IYUV, YUY2, YV12)
HRCHECK(SetOutputType(decoder, MFVideoFormat_NV12));
// now get the sample size
CComPtr<IMFMediaType> type;
HRCHECK(decoder->GetOutputCurrentType(0, &type));
HRCHECK(type->GetUINT32(MF_MT_SAMPLE_SIZE, &sampleSize));
continue;
}
HRCHECK(hr);
LONGLONG time, duration;
HRCHECK(outputSample->GetSampleTime(&time));
HRCHECK(outputSample->GetSampleDuration(&duration));
wprintf(L"Sample time: %I64u ms duration: %I64u ms\n", time / 10000, duration / 10000);
} while (true);
// close file
CloseHandle(file);
HRCHECK(MFShutdown());
}
CoUninitialize();
return 0;
}
Полный проект доступен здесь https://github.com/smourier/MFDecodeH264
спасибо, я делаю как ваш код. но нет правильного изображения.
-1. Когда я впервые создаю данные IMFSample и Lock 5 МБ, все идет хорошо. -2.и жду 5(или других) секунд и отправляю еще один пакет данных. все идет не так. -3. Я использую VideoProcessor для обработки данных NV12 в RGB. это правильно? какой способ вы предлагаете мне обрабатывать данные NV12? большое спасибо !!!
Использование видеопроцессора для преобразования NV12 в RGB — это нормально, но в остальном я не могу сказать без какого-либо воспроизводящего кода.
Спасибо . и последний вопрос: может ли DirectX напрямую отображать yuv(nv12)? или мне нужно преобразовать его в RGB?
Нет, вам нужно будет конвертировать, используя Learn.microsoft.com/en-us/windows/win32/medfound/colorconverter или Learn.microsoft.com/en-us/windows/win32/medfound/ … например
Вы настраиваете преобразование (формат, размеры и т. д.), чтобы знать размер входной выборки или, в более общем плане, то, что вы вводите в качестве входных данных. Можете ли вы уточнить свой вопрос?