美文网首页
使用Gstreamer 作为数据源输出视频数据 VI 集成gst

使用Gstreamer 作为数据源输出视频数据 VI 集成gst

作者: Charles_linzc | 来源:发表于2021-11-22 18:17 被阅读0次

    VcamSource 接口

    在VcamSource的数据源接口中,我们提供了6个必要的接口 :

    gshort vcam_source_start(VcamSource* self);
    
    void  vcam_source_get_mediatype(VcamSource* self, VcamMediaInfo* info);
    
    void  vcam_source_pull_sample(VcamSource* self, GstSample* sample);
    
    void  vcam_source_pull_preroll(VcamSource* self, GstSample* sample);
    
    void  vcam_source_pull_sample2(VcamSource* self, GstMapInfo* map);
    
    void  vcam_source_pull_preroll2(VcamSource* self, GstMapInfo* map);
    

    start接口用来启动gstreamer的pipleLine, 它应该在dll被启动的时候调用,并且在dll退出的时候调用unref.
    vcam_source_get_mediatype用来获取媒体类型,用于directshow组件的连接。
    其余4个都是获取数据接口。我们主要使用vcam_source_pull_sample2来获取自动生成的数据。

    我们需要将库和头文件引入directshow项目中,同时将该项目的静态图,加入连接配置:


    image.png
    image.png

    gst-vcam集成VcamSource

    dllmain的修改

    在dllmain.cpp中引入头文件VcamSource.h, 在入口函数dllmain处,我们需要调用gstrreamer的初始化函数,确保只调用一次。

    BOOL APIENTRY DllMain( HMODULE hModule,
                           DWORD  ul_reason_for_call,
                           LPVOID lpReserved
                         )
    {
        gst_init(NULL, NULL);
        return DllEntryPoint((HINSTANCE)(hModule), ul_reason_for_call, lpReserved);
    }
    

    gst-vcam.h的修改

    增加一个私有属性, VcamSource指针,用来维护对vcamsoure的引用,另外增加析构函数的声明:

    class CVCam : public CSource
    {
    ......
           CVCam::~CVCam();
    protected:
        CVCamStream* stream = nullptr;
            //reference to gstreamer source
        VcamSource* source=nullptr;
    
    };
    

    CVCam类的修改

    修改CVCam,在返回cvcam的时候,同时创建一个VcamSource的实例,这个实例有CVCam保存:

    CVCam::CVCam(LPUNKNOWN lpunk, HRESULT* phr, const GUID id) :
        CSource(NAME("GST Virtual CAM"), lpunk, id)
    {
        ASSERT(phr);
            .......
         source =(VcamSource*) g_object_new(VCAM_TYPE_SOURCE, NULL);
             vcam_source_start(source);
    }
    

    新增一个析构函数,用来在cvcam退出时,减少VcamSource的引用:

    CVCam::~CVCam()
    {   
        g_object_unref(source);
    }
    

    CVCamStream类的修改

    在CVCamStream类中,我们首先需要完成fillBuffer的工作,source接口提供了vcam_source_pull_sample2方法,方便我们来获取gstreamer生成的数据。

    HRESULT CVCamStream::FillBuffer(IMediaSample* pms) {
        REFERENCE_TIME rtNow;
    
        REFERENCE_TIME avgFrameTime = ((VIDEOINFOHEADER*)m_mt.pbFormat)->AvgTimePerFrame;  //获取每帧播放间隔
    
        rtNow = m_rtLastTime;  //获取上次计算的最后时间
        m_rtLastTime += avgFrameTime;  //计算本帧结束时间
        pms->SetTime(&rtNow, &m_rtLastTime);  //这只本真起始时间和结束时间
        pms->SetSyncPoint(TRUE);  //按本帧时间同步
    
        BYTE* pData;
        long lDataLen;
        pms->GetPointer(&pData);  //获取buffer数据块指针
       /***
          在本初填充真实数据
     **/
        GstMapInfo map;
        vcam_source_pull_sample2(parent->source, &map);
        if (map.data == NULL) {
            return ERROR_EMPTY;
        }
        int leinght= map.size;
        if (pms->GetSize() < map.size) leinght = pms->GetSize();
    
        for (int i= 0; i < leinght; ++i) {
            pData[i] = map.data[i];
        }
        for (int i = leinght; i < pms->GetSize(); ++i) {
            pData[i] = rand();
        }
    
        //g_object_unref(map);
        return NOERROR;
    }
    

    这里通过GstMapInfo map获取来在gstreamer的数据,然后把他填充到directshow的buffer里。二外做的循环,时为了测试数据帧大小不一致时,使用随机数填充,方便界面发现。实际验证时可去除。

    除了FillBuffer外,另外两个重要修改的方法时GetStreamCaps和GetMediaType,这里暂时不调用vcam_source_get_mediatype,减少验证复杂性:

    HRESULT STDMETHODCALLTYPE CVCamStream::GetStreamCaps(int iIndex,
        AM_MEDIA_TYPE** pmt, BYTE* pSCC)
    {
        if (iIndex < 0)
            return E_INVALIDARG;
    
        *pmt = CreateMediaType(&m_mt); //创建媒体类型
        DECLARE_PTR(VIDEOINFOHEADER, pvi, (*pmt)->pbFormat); //创建format类型
    
            //设置format
        pvi->bmiHeader.biWidth = 320;
        pvi->bmiHeader.biHeight = 240;
        pvi->AvgTimePerFrame = 333333;
        pvi->bmiHeader.biCompression = MAKEFOURCC('Y', 'U', 'Y', '2');
        //pvi->bmiHeader.biCompression = BI_RGB;
        pvi->bmiHeader.biBitCount = 16;
        //pvi->bmiHeader.biBitCount = 24;
        pvi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        pvi->bmiHeader.biPlanes = 1;
        //pvi->bmiHeader.biSizeImage = pvi->bmiHeader.biWidth *
        //  pvi->bmiHeader.biHeight * 2;
        pvi->bmiHeader.biSizeImage = GetBitmapSize(&pvi->bmiHeader);
        pvi->bmiHeader.biClrImportant = 0;
    
        SetRectEmpty(&(pvi->rcSource));
        SetRectEmpty(&(pvi->rcTarget));
        //设置meida Type
        (*pmt)->majortype = MEDIATYPE_Video;
        (*pmt)->subtype = MEDIASUBTYPE_YUY2;
        //(*pmt)->subtype = MEDIASUBTYPE_RGB24;
        (*pmt)->formattype = FORMAT_VideoInfo;
        (*pmt)->bTemporalCompression = FALSE;
        (*pmt)->bFixedSizeSamples = FALSE;
        (*pmt)->lSampleSize = pvi->bmiHeader.biSizeImage;
        (*pmt)->cbFormat = sizeof(VIDEOINFOHEADER);
        //创建VIDEO_STREAM_CONFIG_CAPS结构体
        DECLARE_PTR(VIDEO_STREAM_CONFIG_CAPS, pvscc, pSCC);
        //设置VIDEO_STREAM_CONFIG_CAPS
        pvscc->guid = FORMAT_VideoInfo;  //媒体类型为FORMAT_VideoInfo
        pvscc->VideoStandard = AnalogVideo_None;  //不是模拟视频
        pvscc->InputSize.cx = pvi->bmiHeader.biWidth;  //输入宽度
        pvscc->InputSize.cy = pvi->bmiHeader.biHeight; //输入高度
        pvscc->MinCroppingSize.cx = pvi->bmiHeader.biWidth;  //最小可裁剪宽度 相当于不允许水平裁剪
        pvscc->MinCroppingSize.cy = pvi->bmiHeader.biHeight;//最小可裁剪高度
        pvscc->MaxCroppingSize.cx = pvi->bmiHeader.biWidth;//最大可裁剪宽度
        pvscc->MaxCroppingSize.cy = pvi->bmiHeader.biHeight;//最大可裁剪高度
        pvscc->CropGranularityX = pvi->bmiHeader.biWidth;  //水平裁剪增量
        pvscc->CropGranularityY = pvi->bmiHeader.biHeight; //水平裁剪增量
        pvscc->CropAlignX = 0;  //水平对其
        pvscc->CropAlignY = 0;  //垂直对齐
    
        pvscc->MinOutputSize.cx = pvi->bmiHeader.biWidth;   //最小输出宽度
        pvscc->MinOutputSize.cy = pvi->bmiHeader.biHeight;   //最小输出高度度
        pvscc->MaxOutputSize.cx = pvi->bmiHeader.biWidth; // 最大输出宽度
            pvscc->MaxOutputSize.cy = pvi->bmiHeader.biHeight;  //最大输出高度度
        pvscc->OutputGranularityX = 0;  //水平输出变化增量
        pvscc->OutputGranularityY = 0;  //垂直输出变化增量
        pvscc->StretchTapsX = 0;   //不允许tretching
        pvscc->StretchTapsY = 0;    //不允许tretching
        pvscc->ShrinkTapsX = 0;     //不允许Shrink
        pvscc->ShrinkTapsY = 0;     //不允许Shrink
        pvscc->MinFrameInterval = pvi->AvgTimePerFrame;   //最小帧间隔=平均帧播放时间
        pvscc->MaxFrameInterval = pvi->AvgTimePerFrame; //最大帧间隔=平均帧播放时间
        pvscc->MinBitsPerSecond = pvi->bmiHeader.biWidth * pvi->bmiHeader.biHeight
            * 2 * 8 * (10000000 / pvi->AvgTimePerFrame);  //最小输出数据bit单位
        pvscc->MaxBitsPerSecond = pvi->bmiHeader.biWidth * pvi->bmiHeader.biHeight
            * 2 * 8 * (10000000 / pvi->AvgTimePerFrame); // 最大输出数据bit单位
    
            return S_OK;
    }
    

    上面的媒体类型和format格式,对应在gstramer重,如下:

    gst-launch-1.0 -v videotestsrc ! video/x-raw,width=320,height=240,FORMAT=YUY2,framerate=30/1 ! autovideosink
    

    完成后,build gst-vcam项目,生成 gstvcam.dll ; 使用regsvr32 gstvcam.dll 进行注册,生成gst-vcam虚拟摄像头信息:


    image.png

    使用graphedit, 创建一个测试graph,可以看到,AVI Decompressor被用来作为转化器:


    image.png
    同时,使用gst-launch-1.0工具和grapEdit启动流画面:
    image.png

    基本显示一致,只有一个小区域颜色不一致。

    相关文章

      网友评论

          本文标题:使用Gstreamer 作为数据源输出视频数据 VI 集成gst

          本文链接:https://www.haomeiwen.com/subject/epwvtrtx.html