Bắt màn hình xanh trong ffplay: Truyền trực tuyến màn hình (bề mặt DirectX) dưới dạng video H264 qua luồng RTP bằng Live555


9

Tôi đang cố gắng truyền phát máy tính để bàn (bề mặt DirectX ở định dạng NV12) dưới dạng video H264 qua luồng RTP bằng bộ mã hóa phần cứng của nền tảng phương tiện Live555 & Windows trên Windows10 và hy vọng nó sẽ được hiển thị bằng ffplay (ffmpeg 4.2). Nhưng chỉ nhận được một màn hình màu xanh lá cây như dưới đây,

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

Tôi đã giới thiệu mẫu phương tiện truyền thông-mẫu & mã hóa phương tiện DirectX của MFWebCamToRTP bằng cách sử dụng MFT phần cứng để triển khai FrameSource của live555 và thay đổi nguồn đầu vào thành bề mặt DirectX thay vì webCam.

Đây là một trích đoạn triển khai của tôi cho cuộc gọi lại doGetNextFrame của Live555 để cung cấp các mẫu đầu vào từ bề mặt 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);
}

Phương thức khởi tạo:

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;
    }

lệnh 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

Tôi không biết mình đang thiếu gì, tôi đã cố gắng khắc phục điều này trong gần một tuần mà không có tiến triển gì, và đã thử hầu hết mọi thứ tôi có thể. Ngoài ra, tài nguyên trực tuyến để mã hóa bề mặt DirectX dưới dạng video rất hạn chế.

Bất kỳ trợ giúp sẽ được đánh giá cao.


1
Tôi nghĩ rằng bạn dự đoán không chính xác doGetNextFrame sẽ được gọi lại sau METransformNeedInput. Có lẽ bạn nên lặp bên trong nó cho đến khi bạn nhận được một cuộc gọi ProcessOutput hợp lệ.
VuVirt

hr = event-> GetType (& eventType); switch (eventType) {....} if (! frameSent) {envir (). taskScheduler (). triggerEvent (eventTriggerId, this); } 2 khối trên sẽ chăm sóc tốt cho việc gọi ProcessInput cho đến khi chúng tôi nhận được đầu ra từ bộ mã hóa. Tôi đã xác minh tương tự. @VuVirt
Ram

Vậy điều gì xảy ra khi frameSent là đúng? Bạn có kích hoạt một sự kiện mới trong trường hợp này? Bạn có một tuyên bố "trở lại" sau đó.
VuVirt

@VuVirt Nó tự động được gọi bởi thư viện live555 bên dưới trong một vòng lặp. "ProcessInput" & "ProcessOutput" được gọi thay thế dựa trên sự kiện trong câu lệnh switch. Tôi đang nhận được một luồng liên tục từ ProcessOut, nhưng không chỉ có thể xem nó. Tôi chắc chắn rằng tôi đang đặt chính xác thời gian và thời lượng mẫu.
Ram

1
Bạn có thể cần kiểm tra xem bạn có nhận được MF_E_TRANSFORM_STREAM_CHANGE từ ProcessOutput hay không và xử lý thay đổi định dạng tương ứng.
VuVirt

Câu trả lời:


6

Nó khó hơn nó có vẻ.

Nếu bạn muốn sử dụng bộ mã hóa như bạn đang làm, bằng cách gọi trực tiếp giao diện IMFTransform , bạn phải chuyển đổi các khung RGB thành NV12. Nếu bạn muốn hiệu suất tốt, bạn nên làm điều đó trên GPU. Có thể thực hiện với trình đổ bóng pixel, kết xuất 2 khung hình, kích thước đầy đủ một vào DXGI_FORMAT_R8_UNORM kết xuất mục tiêu với độ sáng, một nửa kích thước vào mục tiêu DXGI_FORMAT_R8G8_UNORM bằng màu sắc và viết hai giá trị pixel để tạo ra các giá trị NV12. Cả hai mục tiêu kết xuất có thể kết xuất thành 2 mặt phẳng có cùng kết cấu NV12, nhưng chỉ kể từ Windows 8.

Phương pháp khác là sử dụng nhà văn chìm . Nó có thể lưu trữ nhiều MFT cùng một lúc để bạn có thể cung cấp kết cấu RGB trong VRAM, trước tiên người viết chìm sẽ chuyển đổi chúng thành NV12 với một MFT (có khả năng là phần cứng độc quyền được trình điều khiển bởi GPU, giống như bộ mã hóa), sau đó chuyển đến bộ mã hóa MFT. Việc mã hóa thành tệp mp4 tương đối dễ dàng, sử dụng API MFCreateSinkWriterFromURL để tạo trình ghi. Tuy nhiên, khó hơn nhiều để lấy các mẫu thô ra khỏi trình ghi chìm, bạn phải triển khai một phương tiện truyền thông tùy chỉnh, chìm luồng tùy chỉnh cho luồng video của nó và gọi MFCreateSinkWriterFromMediaSink để tạo trình soạn thảo.

Còn nữa.

Bất kể phương pháp mã hóa, bạn không thể sử dụng lại kết cấu khung. Mỗi khung hình bạn nhận được từ DD, bạn nên tạo một kết cấu mới và chuyển nó cho MF.

Bộ mã hóa video mong đợi tốc độ khung hình không đổi. DD không cung cấp cho bạn điều đó, nó cung cấp cho bạn một khung hình mỗi khi có gì đó thay đổi trên màn hình. Có thể là 144 FPS nếu bạn có màn hình chơi game, có thể là 2 FPS nếu thay đổi duy nhất là con trỏ nhấp nháy. Tốt nhất, bạn nên gửi khung cho MF ở tốc độ khung không đổi, được chỉ định trong loại phương tiện video của bạn.

Nếu bạn muốn phát trực tuyến đến mạng, thường xuyên hơn bạn không phải cung cấp các bộ tham số. Trừ khi bạn đang sử dụng bộ mã hóa phần cứng h265 của Intel mà không có nhận xét nào từ Intel , thì MF cung cấp cho bạn dữ liệu đó trong thuộc tính MF_MT_MPEG_SEQUENCE_HEADER của loại phương tiện, bằng cách gọi SetCienMediaType trên giao diện IMFMediaTypeHandler. Bạn có thể thực hiện giao diện đó để nhận thông báo. Bạn sẽ chỉ nhận được dữ liệu đó sau khi bạn bắt đầu mã hóa. Đó là nếu bạn sử dụng một trình soạn thảo chìm, để IMFTransformphương thức dễ dàng hơn, bạn nên lấy MF_E_TRANSFORM_STREAM_CHANGEmã từ ProcessOutputphương thức, sau đó gọi GetOutputAvailableTypeđể nhận loại phương tiện cập nhật với blob ma thuật đó.


bạn có nghĩa là DirectX (Sao chép trên máy tính để bàn) không phân phối các khung ở định dạng NV12 ngay cả khi thiết bị được đặt nội bộ với D3D11_CREATE_DEVICE_VIDEO_SUPPORT & mô tả bề mặt cho DXGI_FORMAT_NV12 và đặt MFT_MESSAGE_SET_D3D_MANAGER trong biến đổi? Tôi cũng nghĩ rằng chúng ta phải chuyển đổi rõ ràng bộ đệm RGB thành NV12 hoặc bất kỳ định dạng đầu vào được hỗ trợ nào (Chủ yếu là các biến thể của YUV) hoặc sử dụng một SinkWriter. Nhưng, người này đã có thể đạt được điều đó bằng cách nào đó với chính cách tiếp cận của tôi. stackoverflow.com/questions/43432670/ hy
Ram


1
Sao chép @Ram Desktop luôn cung cấp các khung RGB ở DXGI_FORMAT_B8G8R8A8_UNORMđịnh dạng. Các MFT mã hóa H264 và h265 chỉ hỗ trợ NV12 và một vài cái khác, những cái lạ không kém. Có người phải chuyển đổi. Bạn sử dụng sao chép máy tính để bàn; bạn đã không thể hỗ trợ Windows 7 với nó. Sử dụng một nhà văn chìm. Tôi khá chắc chắn rằng các MFT phần cứng của Intel / Intel để chuyển đổi RGB sang NV12 có hiệu suất năng lượng cao hơn so với ALU shader pixel, chúng có thể được triển khai hoàn toàn bằng phần cứng.
Soonts

Bạn đúng rồi. Chuyển đổi màu sắc phải được thực hiện rõ ràng. github.com/GPUOpen-LologistsAndSDKs/AMF/issues/92 . Tôi đang tiến hành theo hướng đó.
Ram

1
@Ram Nó nên hoạt động, tôi đã làm nó trước đây. Khi DD từ chối cung cấp cho bạn một khung mới vì không có bản cập nhật, bạn có thể tiết kiệm rất nhiều VRAM bằng cách gửi lại kết cấu tương tự cho bộ mã hóa. Chỉ tạo kết cấu mới khi DD có khung mới cho ya. Nhưng mã để phát hiện khi nào bạn nên gửi khung và thời gian chờ đợi không phải là chuyện nhỏ. Tôi đã sử dụng QueryPerformanceCorer để đo thời gian và một số loại trung bình lăn trong vài khung hình cuối cùng để tìm hiểu tôi nên chụp hay tôi nên ngủ. BTW, cách ngủ đúng là IDXGIOutput :: Phương thức WaitForVBlank.
Soonts

1

ffplayđang phàn nàn về các tham số luồng, tôi cho rằng nó không thể nhận SPS / PPS. Bạn chưa đặt chúng trong SDP được mã hóa cứng của mình - xem RFC-3984 và tìm kiếm sprop-parameter-sets. Một ví dụ từ RFC:

m = video 49170 RTP / AVP 98
a = rtpmap: 98 H264 / 90000
a = fmtp: 98 profile-level-id = 42A01E; sprop-tham số-sets = Z0IACpZTBYmI, aMljiA ==

Tôi mạnh mẽ cho rằng ffplayđang mong đợi những điều này trong SDP. Tôi không nhớ cách lấy SPS / PPS từ bộ mã hóa nền tảng phương tiện, nhưng có trong tải trọng mẫu và bạn cần trích xuất chúng bằng cách tra cứu các đơn vị NAL thích hợp hoặc google cách trích xuất dữ liệu bổ sung từ bộ mã hóa - hit đầu tiên tôi nhận được hứa hẹn.


Đó là một điểm hợp lệ. Tôi cũng có một nghi ngờ về SPS / PPS. Tôi vẫn chưa xác minh nó mặc dù. Cảm ơn đã hướng dẫn tôi đến chủ đề MSDN mang lại cho tôi một chút hy vọng.
Ram

@Ram có một đoạn tuyệt vời rằng SPS / PPS có trong tải trọng mẫu, vì vậy tôi sẽ kiểm tra trước.
Rudolf Bundulis

Vâng, tôi hiểu điều đó. Tôi đã có một số kiến ​​thức về việc truy xuất và phân tích cú pháp SPS / PPS trực tiếp ra khỏi bộ mã hóa nền tảng phương tiện khi tôi thử viết mẫu vào một tệp thông qua Mpeg4MediaSink. Tôi sẽ tiến về phía trước theo hướng này.
Ram

1

Soonts cung cấp cho bạn tất cả những điều cần thiết để giải quyết vấn đề của bạn.

Điều đầu tiên bạn cần làm là chuyển đổi định dạng giữa DXGI_FORMAT_B8G8R8A8_UNORM và MFVideoFormat_NV12:

Chuyển đổi định dạng

định dạng thông tin chuyển đổi

Tôi nghĩ sẽ tốt hơn nếu sử dụng shader để thực hiện chuyển đổi định dạng, bởi vì tất cả các kết cấu sẽ ở trong GPU (tốt hơn cho hiệu suất).

Đó là bước đầu tiên bạn cần làm. Bạn sẽ có những người khác để cải thiện chương trình của bạn.


1
Hình ảnh 2x4 lấy 12 byte trong NV12 chứ không phải giá trị độ sáng 24: 8 mà bạn có ở đó, nhưng hình ảnh màu nhỏ gấp đôi, 1x2 pixel, do đó, tổng cộng chỉ có 4 byte cho thông tin màu của hình ảnh 2x4 đó, 2 byte cho U và 2 byte cho V.
Soonts

Có, bạn đã đúng, tôi đã bỏ qua phần lấy mẫu xuống 4.2.0 của định dạng NV12. Tôi sẽ cố gắng để làm cho một sơ đồ phù hợp hơn.
mofo77
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.