รับหน้าจอสีเขียวใน ffplay: สตรีมเดสก์ท็อป (พื้นผิว DirectX) เป็นวิดีโอ H264 ผ่านสตรีม RTP โดยใช้ Live555


9

ฉันกำลังพยายามสตรีมเดสก์ท็อป (พื้นผิว DirectX ในรูปแบบ NV12) เป็นวิดีโอ H264 ผ่านสตรีม RTP โดยใช้ตัวเข้ารหัสฮาร์ดแวร์ของ Live555 & Windows Media Foundation บน Windows10 และคาดว่าจะแสดงผลโดย ffmpeg (ffmpeg 4.2) แต่จะได้รับหน้าจอสีเขียวเหมือนด้านล่าง

ป้อนคำอธิบายรูปภาพที่นี่

ป้อนคำอธิบายรูปภาพที่นี่

ป้อนคำอธิบายรูปภาพที่นี่

ป้อนคำอธิบายรูปภาพที่นี่

ฉันเรียกMFWebCamToRTP mediafoundation- ตัวอย่าง & การเข้ารหัสพื้นผิว DirectX โดยใช้ฮาร์ดแวร์ MFTสำหรับการใช้ FrameSource ของ 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

SDP:

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
ฉันคิดว่าคุณคาดหวังอย่างไม่ถูกต้องว่า doGetNextFrame จะถูกเรียกอีกครั้งหลังจาก METransformNeedInput บางทีคุณควรวนซ้ำภายในจนกว่าคุณจะได้รับการเรียก ProcessOutput ที่ถูกต้อง
VuVirt

hr = event-> GetType (& eventType); switch (eventType) {.... } ถ้า (! frameSent) {envir (). taskScheduler (). triggerEvent (eventTriggerId, สิ่งนี้); } บล็อก 2 ข้างต้นเป็นอย่างดีดูแลการเรียก ProcessInput จนกว่าเราจะได้รับผลลัพธ์จากการเข้ารหัส ฉันได้ตรวจสอบเดียวกัน @VuVirt
Ram

แล้วจะเกิดอะไรขึ้นเมื่อ frameSent เป็นจริง คุณเริ่มต้นเหตุการณ์ใหม่ในกรณีนี้หรือไม่? คุณมีคำสั่ง "ส่งคืน" หลังจากนั้น
VuVirt

@VuVirt มันถูกเรียกโดยอัตโนมัติโดยไลบรารี live555 พื้นฐานในวง "ProcessInput" & "ProcessOutput" เรียกอีกอย่างหนึ่งตามเหตุการณ์ในคำสั่งเปลี่ยน ฉันได้รับกระแสข้อมูลอย่างต่อเนื่องจาก ProcessOut แต่ไม่สามารถดูได้ ฉันแน่ใจว่าฉันตั้งเวลาและระยะเวลาตัวอย่างได้อย่างถูกต้อง
Ram

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 ได้ผู้เขียน sink จะแปลงเป็น NV12 เป็น M12 เป็นครั้งแรก (ซึ่งมีแนวโน้มว่าจะเป็นฮาร์ดแวร์ที่เป็นกรรมสิทธิ์ซึ่งถูกนำมาใช้โดยไดรเวอร์ GPU เช่นเดียวกับตัวเข้ารหัส) ส่งไปยังโปรแกรมเข้ารหัส MFT การเข้ารหัสเป็นไฟล์ mp4 ทำได้ง่ายใช้MFCreateSinkWriterFromURL API เพื่อสร้างตัวเขียน มันยากกว่ามากที่จะดึงตัวอย่างดิบออกจากตัวเขียน sink อย่างไรก็ตามคุณต้องใช้ sink สื่อแบบกำหนดเอง, stream แบบกำหนดเองสำหรับวิดีโอสตรีมและเรียกMFCreateSinkWriterFromMediaSink MFCเพื่อสร้างตัวเขียน

ยังมีอีก.

ไม่ว่าจะใช้วิธีการเข้ารหัสแบบใดคุณจะไม่สามารถใช้พื้นผิวเฟรมซ้ำได้ แต่ละเฟรมที่คุณได้รับจาก 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เพื่อรับประเภทสื่อที่อัปเดตด้วย blob เวทมนตร์นั้น


คุณหมายถึง DirectX (การทำสำเนาบนเดสก์ท็อป) ไม่ส่งเฟรมในรูปแบบ NV12 แม้ว่าอุปกรณ์จะทำการปรับภาพด้วย D3D11_CREATE_DEVICE_VIDEO_SUPPORT และตัวบอกพื้นผิวเป็น DXGI_FORMAT_NV12 และตั้งค่า MFT_MESSAGE_SET_D3D_MANAGER ฉันคิดว่าเราต้องแปลงบัฟเฟอร์ RGB เป็น NV12 หรือรูปแบบอินพุตที่สนับสนุน (ตัวแปรส่วนใหญ่ของ YUV) หรือใช้ SinkWriter แต่บุคคลนี้สามารถบรรลุผลนั้นด้วยวิธีของฉันเอง stackoverflow.com/questions/43432670/…
Ram


1
การทำสำเนา @Ram Desktop มอบเฟรม RGB ในDXGI_FORMAT_B8G8R8A8_UNORMรูปแบบเสมอ ตัวเข้ารหัส H264 และ h265 MFTs รองรับเฉพาะ NV12 และคู่อื่น ๆ เท่านั้น บางคนต้องเปลี่ยน คุณใช้การทำสำเนาเดสก์ท็อป คุณไม่สามารถรองรับ Windows 7 ได้ ใช้ซิงค์นักเขียน ฉันค่อนข้างแน่ใจว่า MFTs ฮาร์ดแวร์ของ nVidia / Intel เหล่านี้เพื่อแปลง RGB เป็น NV12 นั้นให้พลังงานที่มีประสิทธิภาพมากกว่า ALU pixel shader พวกเขาอาจนำไปใช้กับฮาร์ดแวร์อย่างหมดจด
Soonts

คุณพูดถูก การแปลงสีจะต้องทำอย่างชัดเจน github.com/GPUOpen-LibrariesAndSDKs/AMF/issues/92 ฉันกำลังไปในทิศทางนั้น
ราม

1
@Ram มันควรจะทำงานฉันทำมันมาก่อน เมื่อ DD ปฏิเสธที่จะให้เฟรมใหม่แก่คุณเนื่องจากไม่มีการอัพเดตคุณสามารถบันทึก VRAM จำนวนมากได้โดยส่งพื้นผิวเดียวกันไปยังเครื่องเข้ารหัสอีกครั้ง สร้างพื้นผิวใหม่เฉพาะเมื่อ DD มีเฟรมใหม่สำหรับ ya แต่รหัสที่จะตรวจจับเมื่อคุณควรส่งเฟรมและระยะเวลาในการรอนั้นไม่สำคัญ ฉันใช้ QueryPerformanceCounter เพื่อวัดเวลาและค่าเฉลี่ยการหมุนในเฟรมสองสามเฟรมสุดท้ายเพื่อค้นหาว่าฉันควรจับภาพหรือไม่ BTW วิธีนอนหลับที่ถูกต้องคือวิธี IDXGIOutput :: WaitForVBlank
Soonts

1

เนื่องจากffplayมีการร้องเรียนเกี่ยวกับพารามิเตอร์การสตรีมฉันจะถือว่ามันไม่สามารถรับ SPS / PPS ได้ คุณยังไม่ได้ตั้งไว้ในฮาร์ดโค้ด SDP คุณ - ดูRFC-3984sprop-parameter-setsและมองหา ตัวอย่างจาก RFC:

m = วิดีโอ 49170 RTP / AVP 98
a = rtpmap: 98 H264 / 90000
a = fmtp: 98 profile-level-id = 42A01E; sprop-parameter-sets = Z0IACpZTBYmI, aMljiA ==

ฉันขอสมมติ ffplayคาดหวังจะได้สิ่งเหล่านี้ใน SDP ฉันจำไม่ได้ว่าจะรับ SPS / PPS จากสื่อพื้นฐานของการเข้ารหัสได้อย่างไร แต่อาจอยู่ในส่วนของข้อมูลตัวอย่างและคุณต้องแยกข้อมูลเหล่านั้นโดยค้นหาหน่วย NAL ที่เหมาะสมหรือ Google วิธีดึงข้อมูลเพิ่มเติมจาก encoder - เพลงฮิตครั้งแรกที่ฉันดูดี


มันเป็นจุดที่ถูกต้อง ฉันก็สงสัยใน SPS / PPS ด้วย ฉันยังไม่ยืนยันมัน ขอบคุณที่นำฉันไปที่เธรด MSDN ซึ่งทำให้ฉันมีความหวัง
ราม

@Ram มีช่วงเวลาที่ดีที่ SPS / PPS อยู่ในส่วนของข้อมูลตัวอย่างดังนั้นฉันจะตรวจสอบก่อน
Rudolfs Bundulis

ใช่ฉันเข้าใจว่า ฉันมีความรู้เกี่ยวกับการดึงและแยกวิเคราะห์ SPS / PPS โดยตรงจากตัวเข้ารหัสพื้นฐานสื่อเมื่อฉันพยายามเขียนตัวอย่างไปยังไฟล์ผ่าน Mpeg4MediaSink ฉันจะเดินหน้าต่อไปในทิศทางนี้
Ram

1

ในไม่ช้าก็ให้สิ่งที่จำเป็นทั้งหมดแก่คุณในการแก้ปัญหาของคุณ

สิ่งแรกที่คุณต้องทำคือการแปลงรูปแบบระหว่าง DXGI_FORMAT_B8G8R8A8_UNORM และ MFVideoFormat_NV12:

การแปลงรูปแบบ

ข้อมูลการแปลงรูปแบบ

ฉันคิดว่าการใช้ shader เพื่อทำการแปลงรูปแบบจะดีกว่าเพราะพื้นผิวทั้งหมดจะยังคงอยู่ใน GPU (ดีกว่าสำหรับประสิทธิภาพ)

เป็นขั้นตอนแรกที่คุณต้องทำ คุณจะมีคนอื่นเพื่อปรับปรุงโปรแกรมของคุณ


1
ภาพ 2x4 ใช้เวลา 12 ไบต์ใน NV12 ไม่ใช่ค่าความสว่าง 24: 8 ที่คุณมี แต่ภาพสีมีขนาดเล็กเป็นสองเท่า, 1x2 พิกเซลดังนั้นเพียง 4 ไบต์เท่านั้นสำหรับข้อมูลสีของภาพ 2x4, 2 ไบต์สำหรับ U และ 2 ไบต์สำหรับ V.
Soonts

ใช่คุณพูดถูกฉันละเว้นการสุ่มตัวอย่างเป็น 4.2.0 ของรูปแบบ NV12 ฉันจะพยายามทำให้ไดอะแกรมเหมาะสมยิ่งขึ้น
mofo77
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.