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

2021-11-22  本文已影响0人  Charles_linzc

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

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

上一篇下一篇

猜你喜欢

热点阅读