You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于DirectShow的虚拟摄像头RTP接收后卡顿问题求助

DirectShow虚拟摄像头卡顿问题排查求助

基于DirectShow开发的虚拟摄像头,原本运行正常。集成uvgRTP库后,通过独立线程接收H264格式的RTP帧,将帧数据存入自定义FrameBuffer队列,再由FillBuffer方法从队列取帧处理。目前前6秒视频播放正常,但之后会出现严重卡顿。已测试确认RTP接收的帧无问题,推测可能是FillBuffer处理速度不足,但无法确定具体原因,寻求有DirectShow虚拟摄像头开发经验的开发者协助排查。

调试发现队列大小在5-12之间波动,未超过20帧,但FillBuffer处理速度未达到理想状态(理想队列大小为1)。

RTP接收及入队代码

void CVCamStream::rtpThreadProc(CVCamStream* obj) {
    constexpr uint16_t LOCAL_PORT = 12480;
    constexpr char LOCAL_ADDRESS[] = "::";
    uvgrtp::context ctx;
    uvgrtp::session* sess = ctx.create_session(LOCAL_ADDRESS);
    int flags = RCE_RECEIVE_ONLY;
    uvgrtp::media_stream* receiver = sess->create_stream(LOCAL_PORT, RTP_FORMAT_H264, flags);

    if (receiver) {
        while (!obj->m_quit_thread) {
            uvgrtp::frame::rtp_frame* frame = receiver->pull_frame();
            if (frame) {
                std::vector<uint8_t> newFrame(frame->payload, frame->payload + frame->payload_len);
                obj->frameBuffer_.Push(newFrame);
                uvgrtp::frame::dealloc_frame(frame);
            }
        }
    }
    if (sess) {
        ctx.destroy_session(sess);
    }
}

FrameBuffer队列实现

class FrameBuffer {
public:
    // Push a new frame onto the queue
    void Push(const std::vector<uint8_t>& frame) {
        // Locks the queue with a mutex to prevent concurrent access issues
        std::unique_lock<std::mutex> lock(mutex_);
        // Adds the new frame to the end of the queue
        queue_.push(frame);
        condVar_.notify_one(); // Notify one waiting thread

        // Debugging: Logs the addition of a new frame and the current queue size
        std::wstringstream wss;
        wss << L"Pushed a frame. Queue size: " << queue_.size() << L"\n";
        OutputDebugString(wss.str().c_str());
    }

    // Pop a frame from the queue
    bool Pop(std::vector<uint8_t>& frame) {
        std::unique_lock<std::mutex> lock(mutex_);
        while (queue_.empty()) {
            condVar_.wait(lock); // Wait until there's a frame
            // Optional: Return false or break if shutting down
        }

        // Once there are at least 100 frames, proceed to pop a frame
        frame = std::move(queue_.front());
        queue_.pop();

        return true;
    }
};

FillBuffer处理代码

HRESULT CVCamStream::FillBuffer(IMediaSample* pms) {
    std::vector<uint8_t> frameData;
    frameBuffer_.Pop(frameData);
    long bufferSize = pms->GetSize();
    if (bufferSize < frameData.size()) {
        return E_FAIL;
    }
    BYTE* pData = nullptr;
    pms->GetPointer(&pData);
    memcpy(pData, frameData.data(), frameData.size());
    pms->SetActualDataLength(frameData.size());
    return NOERROR;
}

媒体类型配置代码

HRESULT CVCamStream::GetMediaType(CMediaType* pMediaType)
{
   
    pMediaType->InitMediaType();
    pMediaType->SetType(&MEDIATYPE_Video);
    pMediaType->SetSubtype(&MEDIASUBTYPE_AVC1);
    pMediaType->SetFormatType(&FORMAT_MPEG2_VIDEO);

    pMediaType->bFixedSizeSamples = FALSE;
    pMediaType->bTemporalCompression = TRUE;

    // Our H.264 codec only supports baseline, we can't extract 
    // width and height for other .264 sources. In this case
    // the value will still be 0. Hence just set any value.
    unsigned uiWidth = 1080;
    unsigned uiHeight = 2280;

    MPEG2VIDEOINFO* mpeg2 = (MPEG2VIDEOINFO*)pMediaType->AllocFormatBuffer(sizeof(MPEG2VIDEOINFO) + 80);
    ZeroMemory(mpeg2, sizeof(MPEG2VIDEOINFO) + 80);

    mpeg2->dwProfile = 66;
    mpeg2->dwLevel = 51;
    mpeg2->dwFlags = 4;

   /* byte sps[] = {
            103, 66, 128, 10, 218, 1, 12, 4, 118, 128, 109, 10, 19, 80
    };*/
    byte pps[] = { 104, 206, 6, 242, 1, 2, 3, 4 };

    byte parameters[] = {
        // SPS length and data (unchanged)
        0, 14,
        103, 66, 128, 10, 218, 1, 12, 4, 118, 128, 109, 10, 19, 80,
        // Updated PPS length and new data
        0, 8, // Note the length change here to 8 because of the new PPS size
        104, 206, 6, 242, 1, 2, 3, 4 // New PPS data
    };

    mpeg2->cbSequenceHeader = sizeof(parameters);
    memcpy(mpeg2->dwSequenceHeader, parameters, sizeof(parameters));

    VIDEOINFOHEADER2* pvi2 = &mpeg2->hdr;
    pvi2->bmiHeader.biBitCount = 12;
    pvi2->bmiHeader.biPlanes = 3;
    pvi2->bmiHeader.biWidth = uiWidth;
    pvi2->bmiHeader.biHeight = uiHeight;
    pvi2->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    pvi2->bmiHeader.biSizeImage = DIBSIZE(pvi2->bmiHeader);
    pvi2->bmiHeader.biCompression = FOURCC("lvca");
    //pvi2->AvgTimePerFrame = m_tFrame;
   // pvi2->AvgTimePerFrame = 100000;
    //pvi2->AvgTimePerFrame = 10000000 / 30;
    //SetRect(&pvi2->rcSource, 0, 0, m_cx, m_cy);
    SetRect(&pvi2->rcSource, 0, 0, uiWidth, uiHeight);
    pvi2->rcTarget = pvi2->rcSource;
    pvi2->dwPictAspectRatioX = uiWidth;
    pvi2->dwPictAspectRatioY = uiHeight;
    //pvi2->dwBitRate = 1500000;


    return NOERROR;

} // GetMediaType

内容的提问来源于stack exchange,提问作者Ajdin Jusic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 02:44:54