Отримання зеленого екрану у режимі ffplay: Потокове передавання робочого столу (поверхня DirectX) як відео H264 через RTP-потік за допомогою Live555


9

Я намагаюся передавати робочий стіл (поверхня DirectX у форматі NV12) як відео H264 через RTP-потік, використовуючи апаратний кодер Live555 & Windows Media Foundation в Windows10, і очікую, що його буде виведено ffplay (ffmpeg 4.2). Але тільки отримати зелений екран, як нижче,

введіть тут опис зображення

введіть тут опис зображення

введіть тут опис зображення

введіть тут опис зображення

Я згадав пробу MFWebCamToRTP mediafoundation & Encoding DirectX з використанням апаратного MFT для реалізації FramedSource Live555 та зміни джерела входу на поверхню DirectX замість webCam.

Ось уривок моєї реалізації для зворотного виклику doGetNextFrame Live555 для подачі вхідних зразків з поверхні DirectX:

virtual void doGetNextFrame()
{
    if (!_isInitialised)
    {
        if (!initialise()) {
            printf("Video device initialisation failed, stopping.");
            return;
        }
        else {
            _isInitialised = true;
        }
    }

    //if (!isCurrentlyAwaitingData()) return;

    DWORD processOutputStatus = 0;
    HRESULT mftProcessOutput = S_OK;
    MFT_OUTPUT_STREAM_INFO StreamInfo;
    IMFMediaBuffer *pBuffer = NULL;
    IMFSample *mftOutSample = NULL;
    DWORD mftOutFlags;
    bool frameSent = false;
    bool bTimeout = false;

    // Create sample
    CComPtr<IMFSample> videoSample = NULL;

    // Create buffer
    CComPtr<IMFMediaBuffer> inputBuffer;
    // Get next event
    CComPtr<IMFMediaEvent> event;
    HRESULT hr = eventGen->GetEvent(0, &event);
    CHECK_HR(hr, "Failed to get next event");

    MediaEventType eventType;
    hr = event->GetType(&eventType);
    CHECK_HR(hr, "Failed to get event type");


    switch (eventType)
    {
    case METransformNeedInput:
        {
            hr = MFCreateDXGISurfaceBuffer(__uuidof(ID3D11Texture2D), surface, 0, FALSE, &inputBuffer);
            CHECK_HR(hr, "Failed to create IMFMediaBuffer");

            hr = MFCreateSample(&videoSample);
            CHECK_HR(hr, "Failed to create IMFSample");
            hr = videoSample->AddBuffer(inputBuffer);
            CHECK_HR(hr, "Failed to add buffer to IMFSample");

            if (videoSample)
            {
                _frameCount++;

                CHECK_HR(videoSample->SetSampleTime(mTimeStamp), "Error setting the video sample time.\n");
                CHECK_HR(videoSample->SetSampleDuration(VIDEO_FRAME_DURATION), "Error getting video sample duration.\n");

                // Pass the video sample to the H.264 transform.

                hr = _pTransform->ProcessInput(inputStreamID, videoSample, 0);
                CHECK_HR(hr, "The resampler H264 ProcessInput call failed.\n");

                mTimeStamp += VIDEO_FRAME_DURATION;
            }
        }

        break;

    case METransformHaveOutput:

        {
            CHECK_HR(_pTransform->GetOutputStatus(&mftOutFlags), "H264 MFT GetOutputStatus failed.\n");

            if (mftOutFlags == MFT_OUTPUT_STATUS_SAMPLE_READY)
            {
                MFT_OUTPUT_DATA_BUFFER _outputDataBuffer;
                memset(&_outputDataBuffer, 0, sizeof _outputDataBuffer);
                _outputDataBuffer.dwStreamID = outputStreamID;
                _outputDataBuffer.dwStatus = 0;
                _outputDataBuffer.pEvents = NULL;
                _outputDataBuffer.pSample = nullptr;

                mftProcessOutput = _pTransform->ProcessOutput(0, 1, &_outputDataBuffer, &processOutputStatus);

                if (mftProcessOutput != MF_E_TRANSFORM_NEED_MORE_INPUT)
                {
                    if (_outputDataBuffer.pSample) {

                        //CHECK_HR(_outputDataBuffer.pSample->SetSampleTime(mTimeStamp), "Error setting MFT sample time.\n");
                        //CHECK_HR(_outputDataBuffer.pSample->SetSampleDuration(VIDEO_FRAME_DURATION), "Error setting MFT sample duration.\n");

                        IMFMediaBuffer *buf = NULL;
                        DWORD bufLength;
                        CHECK_HR(_outputDataBuffer.pSample->ConvertToContiguousBuffer(&buf), "ConvertToContiguousBuffer failed.\n");
                        CHECK_HR(buf->GetCurrentLength(&bufLength), "Get buffer length failed.\n");
                        BYTE * rawBuffer = NULL;

                        fFrameSize = bufLength;
                        fDurationInMicroseconds = 0;
                        gettimeofday(&fPresentationTime, NULL);

                        buf->Lock(&rawBuffer, NULL, NULL);
                        memmove(fTo, rawBuffer, fFrameSize);

                        FramedSource::afterGetting(this);

                        buf->Unlock();
                        SafeRelease(&buf);

                        frameSent = true;
                        _lastSendAt = GetTickCount();

                        _outputDataBuffer.pSample->Release();
                    }

                    if (_outputDataBuffer.pEvents)
                        _outputDataBuffer.pEvents->Release();
                }

                //SafeRelease(&pBuffer);
                //SafeRelease(&mftOutSample);

                break;
            }
        }

        break;
    }

    if (!frameSent)
    {
        envir().taskScheduler().triggerEvent(eventTriggerId, this);
    }

    return;

done:

    printf("MediaFoundationH264LiveSource doGetNextFrame failed.\n");
    envir().taskScheduler().triggerEvent(eventTriggerId, this);
}

Метод ініціалізації:

bool initialise()
{
    HRESULT hr;
    D3D11_TEXTURE2D_DESC desc = { 0 };

    HDESK CurrentDesktop = nullptr;
    CurrentDesktop = OpenInputDesktop(0, FALSE, GENERIC_ALL);
    if (!CurrentDesktop)
    {
        // We do not have access to the desktop so request a retry
        return false;
    }

    // Attach desktop to this thread
    bool DesktopAttached = SetThreadDesktop(CurrentDesktop) != 0;
    CloseDesktop(CurrentDesktop);
    CurrentDesktop = nullptr;
    if (!DesktopAttached)
    {
        printf("SetThreadDesktop failed\n");
    }

    UINT32 activateCount = 0;

    // h264 output
    MFT_REGISTER_TYPE_INFO info = { MFMediaType_Video, MFVideoFormat_H264 };

    UINT32 flags =
        MFT_ENUM_FLAG_HARDWARE |
        MFT_ENUM_FLAG_SORTANDFILTER;

    // ------------------------------------------------------------------------
    // Initialize D3D11
    // ------------------------------------------------------------------------

    // Driver types supported
    D3D_DRIVER_TYPE DriverTypes[] =
    {
        D3D_DRIVER_TYPE_HARDWARE,
        D3D_DRIVER_TYPE_WARP,
        D3D_DRIVER_TYPE_REFERENCE,
    };
    UINT NumDriverTypes = ARRAYSIZE(DriverTypes);

    // Feature levels supported
    D3D_FEATURE_LEVEL FeatureLevels[] =
    {
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
        D3D_FEATURE_LEVEL_9_1
    };
    UINT NumFeatureLevels = ARRAYSIZE(FeatureLevels);

    D3D_FEATURE_LEVEL FeatureLevel;

    // Create device
    for (UINT DriverTypeIndex = 0; DriverTypeIndex < NumDriverTypes; ++DriverTypeIndex)
    {
        hr = D3D11CreateDevice(nullptr, DriverTypes[DriverTypeIndex], nullptr,
            D3D11_CREATE_DEVICE_VIDEO_SUPPORT,
            FeatureLevels, NumFeatureLevels, D3D11_SDK_VERSION, &device, &FeatureLevel, &context);
        if (SUCCEEDED(hr))
        {
            // Device creation success, no need to loop anymore
            break;
        }
    }

    CHECK_HR(hr, "Failed to create device");

    // Create device manager
    UINT resetToken;
    hr = MFCreateDXGIDeviceManager(&resetToken, &deviceManager);
    CHECK_HR(hr, "Failed to create DXGIDeviceManager");

    hr = deviceManager->ResetDevice(device, resetToken);
    CHECK_HR(hr, "Failed to assign D3D device to device manager");


    // ------------------------------------------------------------------------
    // Create surface
    // ------------------------------------------------------------------------
    desc.Format = DXGI_FORMAT_NV12;
    desc.Width = surfaceWidth;
    desc.Height = surfaceHeight;
    desc.MipLevels = 1;
    desc.ArraySize = 1;
    desc.SampleDesc.Count = 1;

    hr = device->CreateTexture2D(&desc, NULL, &surface);
    CHECK_HR(hr, "Could not create surface");

    hr = MFTEnumEx(
        MFT_CATEGORY_VIDEO_ENCODER,
        flags,
        NULL,
        &info,
        &activateRaw,
        &activateCount
    );
    CHECK_HR(hr, "Failed to enumerate MFTs");

    CHECK(activateCount, "No MFTs found");

    // Choose the first available encoder
    activate = activateRaw[0];

    for (UINT32 i = 0; i < activateCount; i++)
        activateRaw[i]->Release();

    // Activate
    hr = activate->ActivateObject(IID_PPV_ARGS(&_pTransform));
    CHECK_HR(hr, "Failed to activate MFT");

    // Get attributes
    hr = _pTransform->GetAttributes(&attributes);
    CHECK_HR(hr, "Failed to get MFT attributes");

    // Unlock the transform for async use and get event generator
    hr = attributes->SetUINT32(MF_TRANSFORM_ASYNC_UNLOCK, TRUE);
    CHECK_HR(hr, "Failed to unlock MFT");

    eventGen = _pTransform;
    CHECK(eventGen, "Failed to QI for event generator");

    // Get stream IDs (expect 1 input and 1 output stream)
    hr = _pTransform->GetStreamIDs(1, &inputStreamID, 1, &outputStreamID);
    if (hr == E_NOTIMPL)
    {
        inputStreamID = 0;
        outputStreamID = 0;
        hr = S_OK;
    }
    CHECK_HR(hr, "Failed to get stream IDs");

     // ------------------------------------------------------------------------
    // Configure hardware encoder MFT
   // ------------------------------------------------------------------------
    CHECK_HR(_pTransform->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER, reinterpret_cast<ULONG_PTR>(deviceManager.p)), "Failed to set device manager.\n");

    // Set low latency hint
    hr = attributes->SetUINT32(MF_LOW_LATENCY, TRUE);
    CHECK_HR(hr, "Failed to set MF_LOW_LATENCY");

    hr = MFCreateMediaType(&outputType);
    CHECK_HR(hr, "Failed to create media type");

    hr = outputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
    CHECK_HR(hr, "Failed to set MF_MT_MAJOR_TYPE on H264 output media type");

    hr = outputType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264);
    CHECK_HR(hr, "Failed to set MF_MT_SUBTYPE on H264 output media type");

    hr = outputType->SetUINT32(MF_MT_AVG_BITRATE, TARGET_AVERAGE_BIT_RATE);
    CHECK_HR(hr, "Failed to set average bit rate on H264 output media type");

    hr = MFSetAttributeSize(outputType, MF_MT_FRAME_SIZE, desc.Width, desc.Height);
    CHECK_HR(hr, "Failed to set frame size on H264 MFT out type");

    hr = MFSetAttributeRatio(outputType, MF_MT_FRAME_RATE, TARGET_FRAME_RATE, 1);
    CHECK_HR(hr, "Failed to set frame rate on H264 MFT out type");

    hr = outputType->SetUINT32(MF_MT_INTERLACE_MODE, 2);
    CHECK_HR(hr, "Failed to set MF_MT_INTERLACE_MODE on H.264 encoder MFT");

    hr = outputType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
    CHECK_HR(hr, "Failed to set MF_MT_ALL_SAMPLES_INDEPENDENT on H.264 encoder MFT");

    hr = _pTransform->SetOutputType(outputStreamID, outputType, 0);
    CHECK_HR(hr, "Failed to set output media type on H.264 encoder MFT");

    hr = MFCreateMediaType(&inputType);
    CHECK_HR(hr, "Failed to create media type");

    for (DWORD i = 0;; i++)
    {
        inputType = nullptr;
        hr = _pTransform->GetInputAvailableType(inputStreamID, i, &inputType);
        CHECK_HR(hr, "Failed to get input type");

        hr = inputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
        CHECK_HR(hr, "Failed to set MF_MT_MAJOR_TYPE on H264 MFT input type");

        hr = inputType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_NV12);
        CHECK_HR(hr, "Failed to set MF_MT_SUBTYPE on H264 MFT input type");

        hr = MFSetAttributeSize(inputType, MF_MT_FRAME_SIZE, desc.Width, desc.Height);
        CHECK_HR(hr, "Failed to set MF_MT_FRAME_SIZE on H264 MFT input type");

        hr = MFSetAttributeRatio(inputType, MF_MT_FRAME_RATE, TARGET_FRAME_RATE, 1);
        CHECK_HR(hr, "Failed to set MF_MT_FRAME_RATE on H264 MFT input type");

        hr = _pTransform->SetInputType(inputStreamID, inputType, 0);
        CHECK_HR(hr, "Failed to set input type");

        break;
    }

    CheckHardwareSupport();

    CHECK_HR(_pTransform->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, NULL), "Failed to process FLUSH command on H.264 MFT.\n");
    CHECK_HR(_pTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, NULL), "Failed to process BEGIN_STREAMING command on H.264 MFT.\n");
    CHECK_HR(_pTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, NULL), "Failed to process START_OF_STREAM command on H.264 MFT.\n");

    return true;

done:

    printf("MediaFoundationH264LiveSource initialisation failed.\n");
    return false;
}


    HRESULT CheckHardwareSupport()
    {
        IMFAttributes *attributes;
        HRESULT hr = _pTransform->GetAttributes(&attributes);
        UINT32 dxva = 0;

        if (SUCCEEDED(hr))
        {
            hr = attributes->GetUINT32(MF_SA_D3D11_AWARE, &dxva);
        }

        if (SUCCEEDED(hr))
        {
            hr = attributes->SetUINT32(CODECAPI_AVDecVideoAcceleration_H264, TRUE);
        }

#if defined(CODECAPI_AVLowLatencyMode) // Win8 only

        hr = _pTransform->QueryInterface(IID_PPV_ARGS(&mpCodecAPI));

        if (SUCCEEDED(hr))
        {
            VARIANT var = { 0 };

            // FIXME: encoder only
            var.vt = VT_UI4;
            var.ulVal = 0;

            hr = mpCodecAPI->SetValue(&CODECAPI_AVEncMPVDefaultBPictureCount, &var);

            var.vt = VT_BOOL;
            var.boolVal = VARIANT_TRUE;
            hr = mpCodecAPI->SetValue(&CODECAPI_AVEncCommonLowLatency, &var);
            hr = mpCodecAPI->SetValue(&CODECAPI_AVEncCommonRealTime, &var);

            hr = attributes->SetUINT32(CODECAPI_AVLowLatencyMode, TRUE);

            if (SUCCEEDED(hr))
            {
                var.vt = VT_UI4;
                var.ulVal = eAVEncCommonRateControlMode_Quality;
                hr = mpCodecAPI->SetValue(&CODECAPI_AVEncCommonRateControlMode, &var);

                // This property controls the quality level when the encoder is not using a constrained bit rate. The AVEncCommonRateControlMode property determines whether the bit rate is constrained.
                VARIANT quality;
                InitVariantFromUInt32(50, &quality);
                hr = mpCodecAPI->SetValue(&CODECAPI_AVEncCommonQuality, &quality);
            }
        }
#endif

        return hr;
    }

Команда ffplay:

ffplay -protocol_whitelist file,udp,rtp -i test.sdp -x 800 -y 600 -profile:v baseline

СДП:

v=0
o=- 0 0 IN IP4 127.0.0.1
s=No Name
t=0 0
c=IN IP4 127.0.0.1
m=video 1234 RTP/AVP 96
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1

Я не знаю, чого мені не вистачає, я майже тиждень намагався виправити це без будь-якого прогресу, і намагався майже все, що міг. Також інтернет-ресурси для кодування поверхні DirectX як відео дуже обмежені.

Будь-яка допомога буде вдячна.


1
Я думаю, що ви неправильно очікуєте, що доGetNextFrame буде викликаний знову після METransformNeedInput. Можливо, вам слід зациклитися всередині нього, поки не отримаєте дійсний виклик ProcessOutput.
VuVirt

hr = event-> GetType (& eventType); перемикач (eventType) {....} if (! frameSent) {envir (). taskScheduler (). ТригерEvent (eventTriggerId, це); } Вищезгадані 2 блоки добре потурбуються про виклик ProcessInput, поки ми не отримаємо вихід з кодера. Я перевірив те саме. @VuVirt
Рам

Отже, що відбувається, коли frameSent є правдою? Чи викликаєте ви в цій справі нову подію? Після цього у вас є заява "повернення".
VuVirt

@VuVirt Це автоматично викликається базовою бібліотекою live555 в циклі. Альтернативно викликаються "ProcessInput" та "ProcessOutput" на основі події в операторі комутатора. Я отримую безперервний потік від ProcessOut, але не просто зможу його переглянути. Я впевнений, що я правильно встановив час та тривалість вибірки.
Рам

1
Можливо, вам доведеться перевірити, чи отримуєте ви MF_E_TRANSFORM_STREAM_CHANGE від ProcessOutput і відповідно змінити формат обробки.
VuVirt

Відповіді:


6

Це важче, ніж здається.

Якщо ви хочете використовувати кодер, як це робите, зателефонувавши безпосередньо до інтерфейсу IMFTransform , вам доведеться конвертувати кадри RGB в NV12. Якщо ви хочете гарної продуктивності, вам слід зробити це на GPU. Можливо зробити з піксельними шейдерами, візуалізувати 2 кадри, повнорозмірний розмір у DXGI_FORMAT_R8_UNORM вивести ціль яскравістю, наполовину розміром у DXGI_FORMAT_R8G8_UNORM цільову з кольором, а також записати два піксельних шейдери для отримання значень NV12. Обидві цілі візуалізації можуть перетворюватися на 2 площини тієї самої текстури NV12, але лише з Windows 8.

Іншим методом є використання мийки . Він може розміщувати декілька MFT одночасно, так що ви можете поставляти RGB текстури у VRAM, протокол протоколу спочатку перетворить їх у NV12 за допомогою одного MFT (це, швидше за все, фірмове обладнання, реалізоване драйвером GPU, як і кодер), а потім перейти до кодера MFT. Кодувати у файл mp4 порівняно просто, використовуйте API MFCreateSinkWriterFromURL для створення записувача. Набагато складніше дістати необроблені зразки з програми мийки, однак, вам доведеться реалізувати користувацьку мийку, власну мийку потоку для свого відеопотоку та викликати MFCreateSinkWriterFromMediaSink, щоб створити програму.

Є ще більше.

Незалежно від методів кодування, ви не можете повторно використовувати текстури кадру. Кожен кадр, отриманий від DD, повинен створити нову текстуру і передати її MF.

Відеокодери очікують постійної частоти кадрів. DD не дає вам цього, він дає вам кадр кожного разу, коли щось змінюється на екрані. Може бути 144 FPS, якщо у вас ігровий монітор, може бути 2 FPS, якщо єдиною зміною є миготливий курсор. В ідеалі вам слід подавати кадри в MF з постійною частотою кадрів, вказаною у вашому відео-медіа-типі.

Якщо ви хочете передавати потік в мережу, частіше за все вам доведеться також подавати набори параметрів. Якщо ви не використовуєте апаратний кодер h265 Intel, який порушено без коментарів від Intel , MF надає ці дані в атрибуті MF_MT_MPEG_SEQUENCE_HEADER медіа-типу, викликаючи SetCurrentMediaType в інтерфейсі IMFMediaTypeHandler. Ви можете реалізувати цей інтерфейс, щоб отримувати сповіщення. Ці дані ви отримаєте лише після початку кодування. Ось якщо ви використовуєте програвач мийки, для IMFTransformспособу це простіше, вам слід отримати MF_E_TRANSFORM_STREAM_CHANGEкод від ProcessOutputметоду, а потім зателефонувати, GetOutputAvailableTypeщоб отримати оновлений тип медіа з цим чарівним крапом.


ви маєте на увазі DirectX (копіювання на робочому столі) не доставляє кадри у форматі NV12, навіть коли пристрій інтимізовано D3D11_CREATE_DEVICE_VIDEO_SUPPORT & дескриптором поверхні DXGI_FORMAT_NV12 та встановленням MFT_MESSAGE_SET_D3D_MANAGER при перетворенні? Я теж думав, що нам доведеться явно перетворити буфер RGB в NV12 або будь-який підтримуваний формат введення (в основному варіанти YUV) або використовувати SinkWriter. Але ця людина змогла якось домогтися цього, моїм самим підходом. stackoverflow.com/questions/43432670/…
Рам


1
Дублювання робочого столу @Ram завжди забезпечує рамки RGB у DXGI_FORMAT_B8G8R8A8_UNORMформаті. MFT-кодери H264 та h265 підтримують лише NV12 та пару інших, однаково дивних. Хтось повинен конвертувати. Ви використовуєте копіювання на робочому столі; Ви вже не можете підтримувати Windows 7. Використовуйте мийку. Я впевнений, що апаратні MFT для nVidia / Intel для перетворення RGB в NV12 більш енергоефективні, ніж піксельні шейдери ALU, вони, ймовірно, реалізовані виключно в апаратному забезпеченні.
Soonts

Ти правий. Перетворення кольору повинно здійснюватися явно. github.com/GPUOpen-LibrariesAndSDKs/AMF/isissue/92 . Я рухаюся в цьому напрямку.
Рам

1
@Ram Це повинно працювати, я це робив і раніше. Коли DD відмовляється надати вам новий кадр, оскільки не було оновлень, ви можете зберегти багато VRAM, подавши знову ту саму текстуру в кодер. Створюйте нові текстури лише тоді, коли DD має новий кадр для вас. Але код для виявлення, коли слід подати кадри і як довго чекати, не є тривіальним. Я використовував QueryPerformanceCounter для вимірювання часу, і якийсь середній показник протягом останніх кількох кадрів, щоб дізнатися, чи повинен я захоплювати, чи повинен я спати. До речі, правильним способом сну є метод IDXGIOutput :: WaitForVBlank.
Soonts

1

Оскільки ffplayскаржиться на параметри потоку, я вважаю, що він не може підібрати SPS / PPS. Ви не встановили їх у своєму жорсткому коді SDP - дивіться RFC-3984 і шукайте sprop-parameter-sets. Приклад з RFC:

m = відео 49170 RTP / AVP 98
a = rtpmap: 98 H264 / 90000
a = fmtp: 98 профіль-level-id = 42A01E; sprop-parameter-sets = Z0IACpZTBYmI, aMljiA ==

Я твердо припускаю ffplay, що очікую цього в СДП. Я не пам’ятаю напам’ять, як отримати SPS / PPS з кодера медіа-фонду, але вони є у зразковому корисному навантаженні, і вам потрібно витягнути їх, шукаючи належні одиниці NAL або google, як витягти зайві дані з кодер - перший удар, який я отримав, виглядав багатообіцяючим.


Це дійсний пункт. У мене теж є підозрюваний на SPS / PPS. Я все ще повинен це перевірити. Дякуємо, що направили мене до потоку MSDN, що дає мені певну надію.
Рам

@Ram є хороший ланцюжок, що SPS / PPS є у зразковому корисному навантаженні, тому я би перевірив це спочатку.
Рудольфс Бундуліс

Так, я це розумію. Коли я намагався писати зразки у файл через Mpeg4MediaSink, я отримав деякі знання щодо отримання та аналізу SPS / PPS безпосередньо з кодерів медіа-фундаменту. Я рухатимусь вперед у цьому напрямку.
Рам

1

Соуни дають вам усі необхідні речі для вирішення вашої проблеми.

Перше, що вам потрібно зробити, це перетворення формату між DXGI_FORMAT_B8G8R8A8_UNORM та MFVideoFormat_NV12:

Перетворення формату

інформація про перетворення формату

Я думаю, що для перетворення формату краще використовувати шейдер, оскільки всі текстури залишаться в GPU (краще для продуктивності).

Це перший крок, який потрібно зробити. У вас будуть інші, щоб покращити вашу програму.


1
Зображення 2x4 займає 12 байт у NV12, а не 24: 8 значень яскравості, які у вас є, але кольорове зображення вдвічі менше, 1x2 пікселів, тому всього лише 4 байти для кольорової інформації цього 2x4 зображення, 2 байти для U та 2 байти за V.
Soonts

Так, ви маєте рацію, я пропустив зменшення розміру до 4.2.0 формату NV12. Я спробую скласти більш придатну схему.
mofo77
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.